值棧
<li>ValueStack對象相當于一個棧,它貫穿整個Action的生命周期,每個Action類的對象實例都會擁有一個ValueStack對象。當Struts2接收到一個*.action請求后,并不是直接調用Action方法,而是先將Action類的相應屬性放到ValueStack對象的頂層節點
值棧也位于內存中,它也是和parameters、request、session、application、attr對象放在一起的。值棧屬于ONGL Context里面的根對象。也就是說它位于整個內存中最最重要的地方,所以叫根對象。根對象和另外五個對象是有區別的,根對象可以省寫#號,比如<s:property value="user.username"/>。值棧的生命周期與request請求相關,每次請求產生一個值棧。默認所有的Action會被自動放到值棧里。
服務器跳轉時共用值棧
假設從一個Action11通過服務器跳轉到Action22的話,就意味著這兩個Action是共享一個值棧的,因為一次請求只使用一個值棧。這時內存中情況是這樣的:首先接收到Action11請求后,會產生一個值棧,在棧頂存放Action11對象以及它所有的屬性。然后經過服務器跳轉到Action22,這時就會把Action22對象壓入值棧的棧頂位置,此時Action11對象以及它的所有屬性就位于棧底了。
取值過程
棧的特征是后進先出。于是首先到棧頂的對象里查找是否存在這個屬性,如果棧頂的Action22對象中不存在這個屬性的話
它就會繼續向下尋找直至棧底對象,一直查找是否存在這個屬性
如果最后找到該屬性的話,那么就會在JSP頁面中通過<s:property value="username"/>輸出屬性值
如果在Action22和Action11都有一個同名的同類型的username屬性的話,那么將輸出Action22中的屬性值
因為它是先從棧頂開始尋找屬性的,值棧的特征就是后進先出,但有個前提:請求過程是通過服務器跳轉的
三個語法
假設此時想要獲取Action11中的username屬性的話,就可以使用值棧的Top語法或者N語法
<li>使用Top語法獲取值棧中的第二個對象的屬性:<s:property value="[1].top.username"/>
<li>使用 N 語法獲取值棧中的第二個對象的屬性:<s:property value="[1].username"/>
<li>使用@語法調用Action中的靜態方法:<s:property value="@vs@getVOMethod()"/>
@vs@get()等價于@vs1@getVOMethod(),指的是棧頂對象的靜態getVOMethod()方法
同理@vs2@getVOMethod()就是取值棧中第二個對象的靜態getVOMethod()方法
客戶端跳轉時使用各自的值棧
假如中間某一個步驟中出現了客戶端跳轉的話,那么兩個Action所使用的就是兩個不同的值棧了。所以在Action22中就不能再使用Action11中的屬性了,在最后跳轉到的JSP頁面中也就無法獲取Action11的屬性了。也即從Action22跳轉到JSP頁面時使用的是redirect的話,那么最后值棧中是沒有任何的Action對象的。這個時候我們可以通過鏈接傳參,比如<result type="redirect">test.jsp?netname=${username}</result>,意思就是取出Action22中的username屬性作為參數,通過瀏覽器地址欄傳遞到JSP頁面中然后使用OGNL中的#號獲取Paraments對象的屬性,即<s:property value="#parameters.netname"/>就可以取到值了。
手工向值棧中壓入對象
正常情況下值棧保存的是Action對象,而我們也可以直接往值棧中添加其它對象,這時可以在Action中添加如下代碼
向值棧中添加對象:
ActionContext.getContext.getValueStack().push(new Student("沈浪",22));
而且我們手工往值棧中添加的Student對象會位于棧頂。這是因為Struts2會首先初始化Action,然后才能調用它的方法。初始化Action的時候,便把Action放到值棧中了,然后在執行它的execute()方法時,就又往值棧中添加了Student對象。
OGNL
OGNL是Object-Graph Navigation Language的縮寫,是一種功能強大的表達式語言。通過它簡單一致的表達式語法,可以存取對象的任意屬性,調用對象的方法,遍歷整個對象的結構圖,實現字段類型轉化等功能。OGNL用得最多的地方就是和Struts2的標簽綁定,也可以在配置文件中通過${}使用OGNL表達式
OGNL中$號的使用
1.在國際化資源文件中,引用OGNL表達式
2.在struts.xml文件中,引用OGNL表達式
OGNL中%號的使用
1.使用%{}可以取出保存在值堆棧中的Action對象,直接調用它的方法
2.如果Action繼承了ActionSupport,那么在頁面標簽中可以使用%{getText('key')}獲取國際化信息
OGNL中#號的使用
OGNL中的#號可以取出堆棧上下文中存放的對象
|名稱|作用|例子|
|---|---|---|
|attr
|用于按request>>session>>application順序訪問其屬性
|#attr.userName相當于按順序從三個范圍讀取userName屬性直到找到為止
|
|request
|包含當前HttpServletRequest的屬性的Map
|#request.userName相當于request.getAttribute("userName")
|
|session
|包含當前HttpSession的屬性的Map
|#session.userName相當于session.getAttribute("userName")
|
|application
|包含當前應用的ServletContext的屬性的Map
|#application.userName相當于application.getAttribute("userName")
|
|parameters
|包含當前HTTP請求參數的Map
|#parameters.id[0]相當于request.getParameter("id")
|
獲取Action中的屬性值或者Action中的對象的某某屬性值
利用<s:property/>標簽可以直接獲取Action中的引用類型user里面的username屬性,同樣可以通過user.address.addr獲取user中引用類型address中的addr屬性的值,像這種一層一層往下傳遞的訪問方式,即所謂的導航,也就是一步步的往下調用。
調用Action的對象里面的普通方法
默認的會把Action放到值棧里面,而值棧在訪問的時候,并不需要值棧的名字。當我們調用
<s:property value="user.getVOMethod()"/>
的時候,它會自動到值棧里面查找Action對象里面有沒有user對象,然后它就發現有user,然后它就再找user里面有沒有getVOMethod()方法,然后它發現有,于是調用getVOMethod().實際上調用User中的getVOMethod()方法的過程與獲取表單中的姓名密碼的方式都是相同的,都是到值棧里面查找,找是否存在user對象,如果存在,接著查找user中是否存在某某屬性或方法.
調用Action中的靜態方法
同樣我們也可以在JSP頁面中寫一個OGNL表達式調用Action中的靜態方法。調用Action中的靜態方法時,與調用user對象的getVOMethod()方法的過程,是截然不同的。此時value的寫法是固定的,以@開頭,后面跟上具體的包名,然后@加上靜態方法
比如
<s:property value="@com.jadyer.action.LoginAction@getStatic()"/>
另外user對象是LoginAction中的一個屬性,這個屬性會自動的放到值棧里面,而值棧調用的時候,不用加上@或者包名等等,所以直接user.getVOMethod()就可以了。
調用JDK類中的靜態方法
可以使用<s:property value="@@floor(46.58)"/>輸出floor()的執行結果這就意味著如果不在@@中指定類的話,默認的就表示Java.lang.Math類當前大多數情況下,我們都不會省略這個類,都會寫全了的,然后在后面加上靜態方法。
集合的偽屬性
OGNL能夠引用集合的一些特殊的屬性,這些屬性并不是JavaBean模式,例如size()、length()。當表達式引用這些屬性時,OGNL會調用相應的方法,這就是偽屬性。
比如獲取List的大小:
<s:property value="testList.size"/>
List的偽屬性:size、isEmpty、iterator
Set的偽屬性:size、isEmpty、iterator
Map的偽屬性:size、isEmpty、keys、values
terator的偽屬性:next、hasNext
Enumeration偽屬性:next、hasNext、nextElement、hasMoreElements
獲取集合中元素的實質就是調用它的toString()方法
它還可以直接獲取集合中的元素,事實上是在調用集合的toString()方法,所以我們可以根據實際情況通過重寫集合的toString()方法來實現個性化輸出,甚至它還可以像訪問數組那樣,直接testList[2]獲取集合中的元素,但這種方法只適用于List,不適用于Map。因為Map的索引是key,不是數值.另外,由于HashSet中的元素是沒有順序的,所以也不能用下標獲取單個元素.
Lambda表達式
補充一下:使用Lambda表達式可以在OGNL中書寫遞歸式子,在幫助中對它有很詳細的說明
打開幫助中的//struts-2.0.14-all//struts-2.0.14//docs//index.html頁面
在左側的Documentation下面點擊Guides鏈接,然后在這個頁面中點擊OGNL,最后跳轉到//struts-2.0.14-all//struts-2.0.14//docs//docs//ognl.html將這個頁面右側的下拉條拖放到最下面,就會看到它的說明了,它舉的例子如下所示.
<s:property value="#fib =:[#this==0 ? 0 : #this==1 ? 1 : #fib(#this-2)+#fib(#this-1)], #fib(11)" />
Lambda表達式的語法是:[...] ,中括號前面有一個冒號,所有東西都在中括號里面寫,也就是說我們只要看到一個冒號跟著一個中括號,就表示這里使用的是Lambda表達式,#this
指的是表達式的參數,所以這個例子可以這樣理解:先判斷這個參數是否等于零,如果等于零,那么它的值最后就是零.如果參數不等于零,就再判斷它是否等于壹。如果參數等于壹,那么它的值最后就是壹.如果參數不等于壹,就繼續調用#fib。注意這里已經用中括號將整體的值賦給了fib.實際上很少能夠用得到Lambda表達式.