一.OGNL的概念 OGNL是Object-Graph Navigation Language的縮寫,全稱為對象圖導航語言,是一種功能強大的表達式語言,它通過簡單一致的語法,可以任意存取對象的屬性或者調用對象的方法,能夠遍歷整個對象的結構圖,實現對象屬性類型的轉換等功能。 Struts 2支持以下幾 ...
一.OGNL的概念
OGNL是Object-Graph Navigation Language的縮寫,全稱為對象圖導航語言,是一種功能強大的表達式語言,它通過簡單一致的語法,可以任意存取對象的屬性或者調用對象的方法,能夠遍歷整個對象的結構圖,實現對象屬性類型的轉換等功能。
Struts 2支持以下幾種表達式語言:
- OGNL(Object-Graph Navigation Language),可以方便地操作對象屬性的開源表達式語言;
- JSTL(JSP Standard Tag Library),JSP 2.0集成的標準的表達式語言;
- Groovy,基於Java平臺的動態語言,它具有時下比較流行的動態語言(如Python、Ruby和Smarttalk等)的一些起特性;
- Velocity,嚴格來說不是表達式語言,它是一種基於Java的模板匹配引擎,具說其性能要比JSP好。
Struts 2預設的表達式語言是OGNL,原因是它相對其它表達式語言具有下麵幾大優勢:
- 支持對象方法調用,如xxx.doSomeSpecial();
- 支持類靜態的方法調用和值訪問,表達式的格式為@[類全名(包括包路徑)]@[方法名 | 值名],例如:@java.lang.String@format('foo %s', 'bar')或@tutorial.MyConstant@APP_NAME;
- 支持賦值操作和表達式串聯,如price=100, discount=0.8, calculatePrice(),這個表達式會返回80;
- 訪問OGNL上下文(OGNL context)和ActionContext;
- 操作集合對象。
二.OGNL的重要知識點
1.OGNL表達式的計算是圍繞OGNL上下文進行的。 OGNL上下文實際上就是一個Map對象,由ognl.OgnlContext類表示。它裡面可以存放很多個JavaBean對象。它有一個上下文根對象。 上下文中的根對象可以直接使用名來訪問或直接使用它的屬性名訪問它的屬性值。否則要加首碼“#key”。 2.Struts2的標簽庫都是使用OGNL表達式來訪問ActionContext中的對象數據的。如:<s:propertyvalue="xxx"/>。 3.Struts2將ActionContext設置為OGNL上下文,並將值棧作為OGNL的根對象放置到ActionContext中。 4.值棧(ValueStack) : 可以在值棧中放入、刪除、查詢對象。訪問值棧中的對象不用“#”。 Struts2總是把當前Action實例放置在棧頂。所以在OGNL中引用Action中的屬性也可以省略“#”。 5.調用ActionContext的put(key,value)放入的數據,需要使用#訪問。補充:
(一)對“上下文”概念的理解
上下文, 英文是context,其完整意思應當是concatenate-text,聯繫文本,在IT行業中譯為上下文其實並不確切也不容易理解,尤其是對於初學者,把它翻譯為“引用池”或者“引用區”更加恰當。 比如在一篇15頁的部門介紹中,中華人民共和國中央廣播電視總局(以下簡稱廣電總局),這裡的以下,就是下文,在第15頁的時候,你看到廣電總局四個字就知道具體是哪個部門而不會弄混,這就是在上文做瞭解釋。 在java的JSP中的內置對象中的PageContext,事實上,它就是本頁面的一個單獨的儲存區域,裡面存放的是各個地方(各個範圍)傳過來的屬性的鍵值對的總匯。比如說,從Application裡面存儲了apptime,appname等等屬性,在Session中又存了sessionid sessionstate等等屬性,在request範圍中又存了username,password等屬性的鍵值對,那麼PageContext就會把所有能得到的屬性全部集中到一個區域里,你可以通過這個小容器,接收和調用到各個範圍傳遞過來的屬性,這就是所謂的”上下文“, 你可以把它記為”引用池“! (二)valuestack,stackContext,ActionContext之間的關係三者之間的關係如下圖所示:
- ActionContext :一次Action調用都會創建一個ActionContext ,調用:ActionContext context = ActionContext.getContext()
- ValueStack :由OGNL框架實現 ,可以把它簡單的看作一個List
- Stack Object:放入stack中的對象,一般是action。
- Stack Context(map):stack上下文,它包含一些列對象,包括request/session/attr/application map等。
- EL:存取對象的任意屬性,調用對象的方法,遍歷整個對象結構圖。
ActionContext是Action上下文,可以得到request session application。
ValueStack是值棧 存放表單中的值。
Stack Context 棧上下文 也是用來存值的。
個人看法,action context 是在action中通過actionSupport類來獲取到,主要作用是獲取request之類的對象,然 後valuestack和stack context都是為了使用OGNL,其中value stack 是stack context的根對象,所以我們在JSP頁面中訪問value stack的內容時,是不用加#,而如果是訪問stack context的其他對象則要加上#。
由於值棧是上下文中的 根對象,因此可以直接訪問。那麼對於值棧中的對象該如何訪問呢?Struts2提供了一個特殊的OGNLPropertyAccessor,它可以自動查找棧內的所有對象(從棧頂到棧底),直接找到一個具有你所查找的屬性的對象。也就是說,對於值棧中的任何對象都可以直接訪問,而不需要使用“#”。
Struts2框架總是把Action實例放在棧頂。因為Action在值棧中,而值棧又是OGNL中的根,所以引用Action的屬性可以省略“#”標記,這也是為什麼我們在結果頁面中可以直接訪問Action的屬性的原因。
OGNL的Stack Context里除了包括ValueStack這個根之外,還包括一些命名對象,這些對象沒有保存在值棧中,而是保存在ActionContext中,因此訪問這些對象需要使用“#”標記。這些命名對象都是Map類型。
- parameters:用於訪問請求參數。如:#parameters['id']或#parameters.id,相當於調用了HttpServletRequest對象的getParameter()方法。註意,parameters本質上是一個使用HttpServletRequest對象中的請求參數構造的Map對象,一量對象被創建(在調用Action實例之前就已經創建好了),它和HttpServletRequest對象就沒有了任何關係。
- request對象:用於訪問HttpServletRequest的屬性。例如#request['foo']或者#request.foo,用於返回HttpServletRequest的getAttribute("foo")方法的返回值。
- session對象:用於訪問HttpSession的屬性。例如#rsession['foo']或者#session.foo,用於返回HttpSession的getAttribute("foo")方法的返回值。
- application對象:用於訪問ServletContext的屬性,例如#application['foo']或者#application.foo,用於返回ServletContext的getAttribute("foo")方法的返回值。
- attr對象:該對象將依次搜索如下對象:PageContext、HttpServletRequest、HttpSession、ServletContext中的屬性。
StackContext“根”對象和普通命名對象的區別在於:
- 訪問StackContext里的命名對象需要在對象名之前添加#首碼。
- 當訪問OGNL的Stack Context里的“根”對象的屬性時,可以省略對象名。
struts2對OGNL上下文的概念又做了進一步擴充,在struts2中,OGNL上下文通常如下所示:
|--request
|
|--application
|
context map---|--OgnlValueStack(root) [ user, action, OgnlUtil, ... ]
|
|--session
|
|--attr
|
|--parameters
在Struts2中,採用標準命名的上下文(Context)來處理OGNL表達式。處理OGNL的頂級對象是一個Map(也叫context
map),而OGNL在這個context中就是一個頂級對象(root)。在用法上,頂級對象的屬性訪問,是不需要任何標記首碼的。而其它非頂級的對象
訪問,需要使用#標記。
Struts2框架把OGNL
Context設置為我們的ActionContext。並且ValueStack作為OGNL的根對象。除value
stack之外,Struts2框架還把代表application、session、request這些對象的Map對象也放到
ActionContext中去。(這也就是Struts2建議在Action類中不要直接訪問Servlet API的原因,它可以通過ActionContext對象來部分代替這些(Servlet API)功能 ,以方便對Action類進行測試!)
三、OGNL中的三個重要符號#、%、$
#符號
#符號的用途一般有三種:
- 訪問非根對象屬性,例如#session.msg表達式,由於Struts 2中值棧被視為根對象,所以訪問其他非根對象時,需要加#首碼。實際上,#相當於ActionContext. getContext();#session.msg表達式相當於ActionContext.getContext().getSession(). getAttribute("msg") 。
- 用於過濾和投影(projecting)集合,如persons.{?#this.age>25},persons.{?#this.name=='pla1'}.{age}[0]。
- 用來構造Map,例如示例中的#{'foo1':'bar1', 'foo2':'bar2'}。
%符號
%符號的用途是在標誌的屬性為字元串類型時,計算OGNL表達式的值,這個類似js中的eval,很暴力。
$符號
$符號主要有兩個方面的用途:
- 在國際化資源文件中,引用OGNL表達式,例如國際化資源文件中的代碼:reg.agerange=國際化資源信息:年齡必須在${min}同${max}之間。
- 在Struts 2框架的配置文件中引用OGNL表達式,例如:
- <validators>
- <field name="intb">
- <field-validator type="int">
- <param name="min">10</param>
- <param name="max">100</param>
- <message>BAction-test校驗:數字必須為${min}為${max}之間!</message>
- </field-validator>
- </field>
- </validators>
四、OGNL使用方式
- 訪問屬性
名字屬性獲取:<s:property value="user.username"/><br>
地址屬性獲取:<s:property value="user.address.addr"/><br>
- 訪問方法
調用值棧中對象的普通方法:<s:property value="user.get()"/><br>
- 訪問靜態屬性和方法
調用Action中的靜態方法:<s:property value="@struts.action.LoginAction@get()"/>
調用JDK中的類的靜態方法:<s:property value="@Java.lang.Math@floor(44.56)"/><br>
調用JDK中的類的靜態方法(同上):<s:property value="@@floor(44.56)"/><br>
調用JDK中的類的靜態方法:<s:property value="@java.util.Calendar@getInstance()"/><br>
調用普通類中的靜態屬性:<s:property value="@struts.vo.Address@TIPS"/><br>
- 訪問構造方法
調用普通類的構造方法:<s:property value="new struts.vo.Student('李曉紅' , '美女' , 3 , 25).username"/>
- 訪問數組
獲取List:<s:property value="testList"/><br>
獲取List中的某一個元素(可以使用類似於數組中的下標獲取List中的內容):
<s:property value="testList[0]"/><br>
獲取Set:<s:property value="testSet"/><br>
獲取Set中的某一個元素(Set由於沒有順序,所以不能使用下標獲取數據):
<s:property value="testSet[0]"/><br> ×
獲取Map:<s:property value="testMap"/><br>
獲取Map中所有的鍵:<s:property value="testMap.keys"/><br>
獲取Map中所有的值:<s:property value="testMap.values"/><br>
獲取Map中的某一個元素(可以使用類似於數組中的下標獲取List中的內容):
<s:property value="testMap['m1']"/><br>
獲取List的大小:<s:property value="testSet.size"/><br>
- 訪問集合 – 投影、選擇(? ^ $)
利用選擇獲取List中成績及格的對象:<s:property value="stus.{?#this.grade>=60}"/><br>
利用選擇獲取List中成績及格的對象的username:
<s:property value="stus.{?#this.grade>=60}.{username}"/><br>
利用選擇獲取List中成績及格的第一個對象的username:
<s:property value="stus.{?#this.grade>=60}.{username}[0]"/><br>
利用選擇獲取List中成績及格的第一個對象的username:
<s:property value="stus.{^#this.grade>=60}.{username}"/><br>
利用選擇獲取List中成績及格的最後一個對象的username:
<s:property value="stus.{$#this.grade>=60}.{username}"/><br>
利用選擇獲取List中成績及格的第一個對象然後求大小:
<s:property value="stus.{^#this.grade>=600}.{username}.size"/><br>
- 集合的偽屬性
OGNL能夠引用集合的一些特殊的屬性,這些屬性並不是JavaBeans模式,例如size(),length()等等. 當表達式引用這些屬性時,OGNL會調用相應的方法,這就是偽屬性.
- Lambda :[…] 格式::[…]
使用Lambda表達式計算階乘:
<s:property value="#f = :[#this==1?1:#this*#f(#this-1)] , #f(4)"/><br>
- OGNL中#的使用 #可以取出堆棧上下文中的存放的對象.
獲取Paraments對象的屬性:<s:property value="#parameters.username"/>
- OGNL中%的使用
用%{}可以取出存在值堆棧中的Action對象,直接調用它的方法.
例如你的Action如果繼承了ActionSupport .那麼在頁面標簽中,用%{getText('key')}的方式可以拿出國際化信息.
OGNL中$的使用
“$”有兩個主要的用途
用於在國際化資源文件中,引用OGNL表達式
在Struts 2配置文件中,引用OGNL表達式
值棧
ValueStack對象。這個對象貫穿整個Action的生命周期(每個Action類的對象實例會擁有一個ValueStack對象)。當Struts 2接收到一個.action的請求後,會先建立Action類的對象實例,但並不會調用Action方法,而是先將Action類的相應屬性放到ValueStack對象的頂層節點(ValueStack對象相當於一個棧)。
在Action中獲得ValueStack對象:ActionContext.getContext().getValueStack()
Top語法:使用Top獲取值棧中的第二個對象:<s:property value="[1].top.對象"/>
N語法:使用N獲取值棧中的第二個對象:<s:property value="[1].對象"/>
@語法:調用action中的靜態方法:<s:property value="@vs1@靜態方法"/> vs:值棧 1:表示第一個。