struts2之OGNL用法
淺析OGNL
OGNL是Object-GraphNavigation Language的縮寫(xiě),是一種功能強(qiáng)大的表達(dá)式語(yǔ)言
通過(guò)它簡(jiǎn)單一致的表達(dá)式語(yǔ)法,可以存取對(duì)象的任意屬性,調(diào)用對(duì)象的方法,遍歷整個(gè)對(duì)象的結(jié)構(gòu)圖,實(shí)現(xiàn)字段類(lèi)型轉(zhuǎn)化等功能
OGNL用得最多的地方就是和Struts2的標(biāo)簽綁定,也可以在配置文件中通過(guò)${}使用OGNL表達(dá)式
?
OGNL中$號(hào)的使用
1..在國(guó)際化資源文件中,引用OGNL表達(dá)式
2..在struts.xml文件中,引用OGNL表達(dá)式
?
OGNL中%號(hào)的使用
1..使用%{}可以取出保存在值堆棧中的Action對(duì)象,直接調(diào)用它的方法
2..如果Action繼承了ActionSupport,那么在頁(yè)面標(biāo)簽中可以使用%{getText('key')}獲取國(guó)際化信息
OGNL中#號(hào)的使用
OGNL中的#號(hào)可以取出堆棧上下文中存放的對(duì)象
| 名稱(chēng) | 作用 | 例子 |
| attr | 用于按request>>session>>application順序訪問(wèn)其屬性 | #attr.userName相當(dāng)于按順序從三個(gè)范圍讀取userName屬性直到找到為止 |
| request | 包含當(dāng)前HttpServletRequest的屬性的Map | #request.userName相當(dāng)于request.getAttribute("userName") |
| session | 包含當(dāng)前HttpSession的屬性的Map | #session.userName相當(dāng)于session.getAttribute("userName") |
| application | 包含當(dāng)前應(yīng)用的ServletContext的屬性的Map | #application.userName相當(dāng)于application.getAttribute("userName") |
| parameters | 包含當(dāng)前HTTP請(qǐng)求參數(shù)的Map | #parameters.id[0]相當(dāng)于request.getParameter("id") |
?
獲取Action中的屬性值或者Action中的對(duì)象的某某屬性值
利用<s:property/>標(biāo)簽可以直接獲取Action中的引用類(lèi)型user里面的username屬性
同樣可以通過(guò)user.address.addr獲取user中引用類(lèi)型address中的addr屬性的值
像這種一層一層往下傳遞的訪問(wèn)方式,即所謂的導(dǎo)航,也就是一步步的往下調(diào)用
?
調(diào)用Action的對(duì)象里面的普通方法
默認(rèn)的會(huì)把Action放到值棧里面,而值棧在訪問(wèn)的時(shí)候,并不需要值棧的名字
當(dāng)我們調(diào)用<s:propertyvalue="user.getVOMethod()"/>的時(shí)候
它會(huì)自動(dòng)到值棧里面查找Action對(duì)象里面有沒(méi)有user對(duì)象,然后它就發(fā)現(xiàn)有user
然后它就再找user里面有沒(méi)有g(shù)etVOMethod()方法,然后它發(fā)現(xiàn)有,于是調(diào)用getVOMethod()
實(shí)際上調(diào)用User中的getVOMethod()方法的過(guò)程與獲取表單中的姓名密碼的方式都是相同的
都是到值棧里面查找,找是否存在user對(duì)象,如果存在,接著查找user中是否存在某某屬性或方法
?
調(diào)用Action中的靜態(tài)方法
同樣我們也可以在JSP頁(yè)面中寫(xiě)一個(gè)OGNL表達(dá)式調(diào)用Action中的靜態(tài)方法
調(diào)用Action中的靜態(tài)方法時(shí),與調(diào)用user對(duì)象的getVOMethod()方法的過(guò)程,是截然不同的
此時(shí)value的寫(xiě)法是固定的,以@開(kāi)頭,后面跟上具體的包名,然后@加上靜態(tài)方法
比如<s:propertyvalue="@com.jadyer.action.LoginAction@getStatic()"/>
另外user對(duì)象是LoginAction中的一個(gè)屬性,這個(gè)屬性會(huì)自動(dòng)的放到值棧里面
而值棧調(diào)用的時(shí)候,不用加上@或者包名等等,所以直接user.getVOMethod()就可以了
?
調(diào)用JDK類(lèi)中的靜態(tài)方法
可以使用<s:propertyvalue="@@floor(46.58)"/>輸出floor()的執(zhí)行結(jié)果
這就意味著如果不在@@中指定類(lèi)的話,默認(rèn)的就表示java.lang.Math類(lèi)
當(dāng)前大多數(shù)情況下,我們都不會(huì)省略這個(gè)類(lèi),都會(huì)寫(xiě)全了的,然后在后面加上靜態(tài)方法
?
集合的偽屬性
OGNL能夠引用集合的一些特殊的屬性,這些屬性并不是JavaBean模式,例如size()、length()
當(dāng)表達(dá)式引用這些屬性時(shí),OGNL會(huì)調(diào)用相應(yīng)的方法,這就是偽屬性
比如獲取List的大小:<s:propertyvalue="testList.size"/>
??????List的偽屬性:size、isEmpty、iterator
???????Set的偽屬性:size、isEmpty、iterator
???????Map的偽屬性:size、isEmpty、keys、values
??Iterator的偽屬性:next、hasNext
Enumeration偽屬性:next、hasNext、nextElement、hasMoreElements
?
獲取集合中元素的實(shí)質(zhì)就是調(diào)用它的toString()方法
它還可以直接獲取集合中的元素,事實(shí)上是在調(diào)用集合的toString()方法
所以我們可以根據(jù)實(shí)際情況通過(guò)重寫(xiě)集合的toString()方法來(lái)實(shí)現(xiàn)個(gè)性化輸出
甚至它還可以像訪問(wèn)數(shù)組那樣,直接testList[2]獲取集合中的元素
但這種方法只適用于List,不適用于Map。因?yàn)镸ap的索引是key,不是數(shù)值
另外,由于HashSet中的元素是沒(méi)有順序的,所以也不能用下標(biāo)獲取單個(gè)元素
?
Lambda表達(dá)式
補(bǔ)充一下:使用Lambda表達(dá)式可以在OGNL中書(shū)寫(xiě)遞歸式子,在幫助中對(duì)它有很詳細(xì)的說(shuō)明
打開(kāi)幫助中的//struts-2.0.14-all//struts-2.0.14//docs//index.html頁(yè)面
在左側(cè)的Documentation下面點(diǎn)擊Guides鏈接,然后在這個(gè)頁(yè)面中點(diǎn)擊OGNL
最后跳轉(zhuǎn)到//struts-2.0.14-all//struts-2.0.14//docs//docs//ognl.html
將這個(gè)頁(yè)面右側(cè)的下拉條拖放到最下面,就會(huì)看到它的說(shuō)明了,它舉的例子如下所示
<s:property value="#fib =:[#this==0? 0 : #this==1 ? 1 : #fib(#this-2)+#fib(#this-1)], #fib(11)" />
Lambda表達(dá)式的語(yǔ)法是:[...]?,中括號(hào)前面有一個(gè)冒號(hào),所有東西都在中括號(hào)里面寫(xiě)
也就是說(shuō)我們只要看到一個(gè)冒號(hào)跟著一個(gè)中括號(hào),就表示這里使用的是Lambda表達(dá)式
#this指的是表達(dá)式的參數(shù)
所以這個(gè)例子可以這樣理解:先判斷這個(gè)參數(shù)是否等于零,如果等于零,那么它的值最后就是零
如果參數(shù)不等于零,就再判斷它是否等于壹。如果參數(shù)等于壹,那么它的值最后就是壹
如果參數(shù)不等于壹,就繼續(xù)調(diào)用#fib。注意這里已經(jīng)用中括號(hào)將整體的值賦給了fib
實(shí)際上很少能夠用得到Lambda表達(dá)式
?
利用投影獲取屬性
利用投影獲取List中對(duì)象的username屬性時(shí),其中{}表示的是一個(gè)集合
stus.{username}就表示將suts中所有的username屬性取出組成一個(gè)新的列表
?
利用選擇獲取屬性
OGNL表達(dá)式是很靈活的,可以同時(shí)使用選擇技術(shù)與投影技術(shù)獲取屬性
使用選擇技術(shù)時(shí),#this代表當(dāng)前元素,問(wèn)號(hào)?是把所有滿足條件的元素都取出來(lái)
上箭頭^是開(kāi)始的意思,所以stus.{^#this.grade>=60}.{username}輸出的是[張三]
注意,此時(shí)輸出文本中包含中括號(hào),這表示它是一個(gè)列表
而stus.{?#this.grade>=60}.{username}[0]輸出的是張三,是字符串,二者是不同的
美元符號(hào)$是結(jié)束的意思,所以stus.{$#this.grade>=60}.{username}輸出的是[王五]
這三個(gè)符合:問(wèn)號(hào)、上箭頭、美元符所返回的都是List
?
?
淺析值棧
ValueStack對(duì)象相當(dāng)于一個(gè)棧,它貫穿整個(gè)Action的生命周期,每個(gè)Action類(lèi)的對(duì)象實(shí)例都會(huì)擁有一個(gè)ValueStack對(duì)象
當(dāng)Struts2接收到一個(gè)*.action請(qǐng)求后,并不是直接調(diào)用Action方法,而是先將Action類(lèi)的相應(yīng)屬性放到ValueStack對(duì)象的頂層節(jié)點(diǎn)
值棧也位于內(nèi)存中,它也是和parameters、request、session、application、attr對(duì)象放在一起的
值棧屬于ONGL Context里面的根對(duì)象。也就是說(shuō)它位于整個(gè)內(nèi)存中最最重要的地方,所以叫根對(duì)象
根對(duì)象和另外五個(gè)對(duì)象是有區(qū)別的,根對(duì)象可以省寫(xiě)#號(hào),比如<s:propertyvalue="user.username"/>
值棧的生命周期與request請(qǐng)求相關(guān),每次請(qǐng)求產(chǎn)生一個(gè)值棧。默認(rèn)所有的Action會(huì)被自動(dòng)放到值棧里
?
服務(wù)器跳轉(zhuǎn)時(shí)共用值棧
假設(shè)從一個(gè)Action11通過(guò)服務(wù)器跳轉(zhuǎn)到Action22的話,就意味著這兩個(gè)Action是共享一個(gè)值棧的,因?yàn)橐淮握?qǐng)求只使用一個(gè)值棧
這時(shí)內(nèi)存中情況是這樣的:首先接收到Action11請(qǐng)求后,會(huì)產(chǎn)生一個(gè)值棧,在棧頂存放Action11對(duì)象以及它所有的屬性
然后經(jīng)過(guò)服務(wù)器跳轉(zhuǎn)到Action22,這時(shí)就會(huì)把Action22對(duì)象壓入值棧的棧頂位置,此時(shí)Action11對(duì)象以及它的所有屬性就位于棧底了
?
取值過(guò)程
棧的特征是后進(jìn)先出。于是首先到棧頂?shù)膶?duì)象里查找是否存在這個(gè)屬性,如果棧頂?shù)腁ction22對(duì)象中不存在這個(gè)屬性的話
它就會(huì)繼續(xù)向下尋找直至棧底對(duì)象,一直查找是否存在這個(gè)屬性
如果最后找到該屬性的話,那么就會(huì)在JSP頁(yè)面中通過(guò)<s:propertyvalue="username"/>輸出屬性值
如果在Action22和Action11都有一個(gè)同名的同類(lèi)型的username屬性的話,那么將輸出Action22中的屬性值
因?yàn)樗窍葟臈m旈_(kāi)始尋找屬性的,值棧的特征就是后進(jìn)先出,但有個(gè)前提:請(qǐng)求過(guò)程是通過(guò)服務(wù)器跳轉(zhuǎn)的
?
三個(gè)語(yǔ)法
假設(shè)此時(shí)想要獲取Action11中的username屬性的話,就可以使用值棧的Top語(yǔ)法或者N語(yǔ)法
使用Top語(yǔ)法獲取值棧中的第二個(gè)對(duì)象的屬性:<s:property value="[1].top.username"/>
使用 N 語(yǔ)法獲取值棧中的第二個(gè)對(duì)象的屬性:<s:propertyvalue="[1].username"/>
另外值棧還有一個(gè)@語(yǔ)法,例如使用@語(yǔ)法調(diào)用Action中的靜態(tài)方法:<s:property value="@vs@getVOMethod()"/>
@vs@get()等價(jià)于@vs1@getVOMethod(),指的是棧頂對(duì)象的靜態(tài)getVOMethod()方法
同理@vs2@getVOMethod()就是取值棧中第二個(gè)對(duì)象的靜態(tài)getVOMethod()方法
?
客戶(hù)端跳轉(zhuǎn)時(shí)使用各自的值棧
假如中間某一個(gè)步驟中出現(xiàn)了客戶(hù)端跳轉(zhuǎn)的話,那么兩個(gè)Action所使用的就是兩個(gè)不同的值棧了
所以在Action22中就不能再使用Action11中的屬性了,在最后跳轉(zhuǎn)到的JSP頁(yè)面中也就無(wú)法獲取Action11的屬性了
也即從Action22跳轉(zhuǎn)到JSP頁(yè)面時(shí)使用的是redirect的話,那么最后值棧中是沒(méi)有任何的Action對(duì)象的
這個(gè)時(shí)候我們可以通過(guò)鏈接傳參,比如<result type="redirect">test.jsp?netname=${username}</result>
意思就是取出Action22中的username屬性作為參數(shù),通過(guò)瀏覽器地址欄傳遞到JSP頁(yè)面中
然后使用OGNL中的#號(hào)獲取Paraments對(duì)象的屬性,即<s:propertyvalue="#parameters.netname"/>就可以取到值了
?
手工向值棧中壓入對(duì)象
正常情況下值棧保存的是Action對(duì)象,而我們也可以直接往值棧中添加其它對(duì)象,這時(shí)可以在Action中添加如下代碼
向值棧中添加對(duì)象:ActionContext.getContext.getValueStack().push(new Student("沈浪",22));
而且我們手工往值棧中添加的Student對(duì)象會(huì)位于棧頂。這是因?yàn)镾truts2會(huì)首先初始化Action,然后才能調(diào)用它的方法
初始化Action的時(shí)候,便把Action放到值棧中了,然后在執(zhí)行它的execute()方法時(shí),就又往值棧中添加了Student對(duì)象
?
補(bǔ)充
1..當(dāng)OGNL取不到值的時(shí)候,它不會(huì)報(bào)錯(cuò),而是什么都不顯示
2..<s:property value="[0]"/>返回的是ValueStack中從上至下的所有的Object
????<s:propertyvalue="[1]"/>返回的是ValueStack中從上至下的第二個(gè)Object
3..<s:property value="[0].username"/>返回的是成員變量username的值
??? 假設(shè)ValueStack中存在兩個(gè)Action的話,如果第一個(gè)Action如果沒(méi)有username變量
??? 那么它會(huì)繼續(xù)找第二個(gè)Action。那么在什么情況下ValueStack中會(huì)存在兩個(gè)Action呢
??? 答案是在struts.xml中配置的是從一個(gè)Action通過(guò)<resulttype="chain">跳轉(zhuǎn)到另一個(gè)Action
4..<constantname="struts.ognl.allowStaticMethodAccess"value="true"/>
??? 在Struts2.1.6中必須設(shè)置struts.ognl.allowStaticMethodAccess為true之后
??? 才允許使用OGNL訪問(wèn)靜態(tài)方法。而在Struts2.0.11則無(wú)需設(shè)置,即可直接訪問(wèn)
?
?
eg:User對(duì)象屬性獲取 如User中有username和password字段獲取username屬性<s:property value="user.username" />
獲取password屬性<s:property value="user.password" />
若User中又包含定義了address對(duì)象,address對(duì)象中包含有addr屬性,則可以這樣訪問(wèn)
獲取addr屬性<s:property value="user.address.addr" />
若User中還包含一個(gè)get()的普通方法,可以這樣調(diào)用
<s:property value="user.get()" />
以上是調(diào)用值棧中對(duì)象的普通方法,user為值棧中的對(duì)象
調(diào)用action中的靜態(tài)方法get(),普通方法不能直接調(diào)用
<s:property value="@com.netshuai.action.ManagerAction@get()" />
以上為調(diào)用非值棧中的靜態(tài)方法
調(diào)用JDK中類(lèi)的靜態(tài)方法<s:property value="@java.lang.Math@floor(32.56)" />
上例也可寫(xiě)成<s:property value="@@floor(32.56)" />,省略前面的類(lèi)則默認(rèn)使用java.lang.Math類(lèi),其他類(lèi)不可省略
調(diào)用普通類(lèi)中的靜態(tài)屬性<s:property value="@com.netshuai.model.Address@city" />
address中的city靜態(tài)屬性要用public聲明
調(diào)用普通類(lèi)的構(gòu)造方法,如構(gòu)造方法為
public User(String username)
{
??????this.username=username;
}
調(diào)用方法為<s:property value="new com.netshuai.model.User('hello').username" />,則返回username值為hello
獲取List<s:property value="list" />
獲取List中的某一個(gè)元素<s:property value="list[0]" />
獲取List的大小<s:property value="list.size" />
獲取Set<s:property value="set" />
無(wú)法獲取Set中的某一個(gè)元素,因?yàn)镾et沒(méi)有順序
獲取Map<s:property value="map" />
獲取Map中所有key的值<s:property value="map.keys" />
獲取Map中所有value的值<s:property value="map.values" />
獲取Map中的某一個(gè)元素<s:property value="map['k1']" />
獲取List所有對(duì)象<s:property value="listObject" />,需要重寫(xiě)toString()方法才能正常顯示對(duì)象的值?
利用投影獲取List中所有對(duì)象的username屬性<s:property value="listObject.{username}" />
利用投影獲取List中第一個(gè)對(duì)象的username屬性<s:property value="listObject.{username}[0]" />
利用選擇獲取List中年齡大于30的對(duì)象<s:property value="listObject.{?#this.age>30}" />
利用選擇獲取List中年齡大于30的對(duì)象的username<s:property value="listObject.{?#this.age>30}.{username}" />
利 用選擇獲取List中年齡大于30的第一個(gè)對(duì)象的username<s:property value="listObject.{?#this.age>30}.{username}[0]" />或<s:property value="listObject.{^#this.age>30}.{username}" />
利用選擇獲取List中年齡大于30的最后一個(gè)對(duì)象的username<s:property value="listObject.{$#this.age>30}.{username}" />
獲取parameters中的屬性<s:property value="#parameters.name" />
獲取request中的屬性<s:property value="#request.name" />
獲取session中的屬性<s:property value="#session.name" />
獲取application中的屬性<s:property value="#application.name" />
<s:property value="#attr.name" />按順序遍歷上面四個(gè)對(duì)象,然后返回首先找到的值
%{}可以取出存在值堆棧中的Action對(duì)象,直接調(diào)用它的方法,如%{getText('key')}可以取出國(guó)際化信息
${}可以用在國(guó)際化資源文件中和struts2配置文件中
使用top獲取值棧中第二個(gè)對(duì)象<s:property value="[1].top.user"/>
使用top獲取值棧中第二個(gè)對(duì)象的屬性<s:property value="[1].user"/>
調(diào)用值棧中action的靜態(tài)方法get()<s:property value="@vs@get()"/>,vs也可寫(xiě)做vs1
調(diào)用值棧中第二個(gè)action的靜態(tài)方法get()<s:property value="@vs2@get()"/>
將一個(gè)對(duì)象放入值棧
ActionContext.getContext().getValueStack().push(user); 總結(jié): 1.當(dāng)使用OGNL調(diào)用靜態(tài)方法的時(shí)候。需要按照如下語(yǔ)法編寫(xiě)表達(dá)式:@package.classname@methodname(parameter)
2.對(duì)于OGNL來(lái)說(shuō)。java.lang.Math是其的默認(rèn)類(lèi)。如果調(diào)用java.lang.Math的靜態(tài)方法時(shí)。無(wú)需指定類(lèi)的名字。比如@@min(4,10)
3.對(duì)于OGNL來(lái)說(shuō),數(shù)組和集合是一樣的。都是通過(guò)下標(biāo)索引來(lái)訪問(wèn)的。構(gòu)造集合的時(shí)候用{...}形式
4.使用OGNL來(lái)映射(Map)的語(yǔ)法格式如下所示:#{'key1':'value1','key2','value2'}
5.過(guò)濾(filtering):conllection.{?,表達(dá)式}這是針對(duì)集合來(lái)處理的。當(dāng)“?”被"^"代替時(shí)表示獲取集合中的第一個(gè)對(duì)象。當(dāng)“?”被"$"代替時(shí)表示獲取集合中的最后一個(gè)對(duì)象。
6.OGNL針對(duì)集合提供了一些偽屬性(如size,isEmpty),讓我們可以通過(guò)屬性的方式來(lái)調(diào)用方法。(本質(zhì)原因在于集合當(dāng)中的很多方法并不符合javaBean的命名規(guī)則),但我們依然可以通過(guò)調(diào)用方法來(lái)實(shí)現(xiàn)與偽屬性相同的目的
7.在使用過(guò)濾操作的時(shí)候。我們通常使用#this.該表達(dá)式用于代表當(dāng)前正在迭代的集合中的對(duì)象
8.過(guò)濾(projection):collection.{expression}
9.過(guò)濾與投影的區(qū)別:類(lèi)比于數(shù)據(jù)庫(kù)中的表。過(guò)濾是取行的操作,而投影是取列的操作。
10.在struts2中有個(gè)稱(chēng)之為值棧的概念(valueStack)
11.在struts2中,根對(duì)象就是valueStack,在struts2的任何流程中。valueStack中的最頂層對(duì)象一定是Action對(duì)象。
12.訪問(wèn)靜態(tài)方法或是靜態(tài)成員變量的:@vs@method
13.關(guān)于struts2標(biāo)簽庫(kù)屬性值的%和#號(hào)的關(guān)系,如果標(biāo)簽的屬性值是OGNL表達(dá)式,那么無(wú)需加上%{} 如果 標(biāo)簽的屬性值的是字符串類(lèi)型的。那么在字符串當(dāng)中凡是出現(xiàn)%{}都會(huì)被解析成OGNL表達(dá)式,解析完畢后再與其他的字符串進(jìn)行拼接構(gòu)造出最后的字符串值。我們可以再所有的屬性值加上%{},這樣如果該屬性值是OGNL表達(dá)式,那么標(biāo)簽處理類(lèi)就會(huì)將%{}忽略掉
轉(zhuǎn)載于:https://www.cnblogs.com/sxxjyj/p/6093405.html
總結(jié)
以上是生活随笔為你收集整理的struts2之OGNL用法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 在ueditor编辑器的光标停留处插入内
- 下一篇: 微积分5--隐函数