ValueStack實際上就是對OGNL的封裝,OGNL主要的功能就是賦值與取值。
ValueStack中的數據,分兩個部分存放:root和context
同時ValueStack暴露相關的接口(賦值和取值):
void setValue(String expr, Object value);
Object findValue(String expr);
通過OGNL表達式對ValueStack中的數據進行操作。
root
ValueStack中的root對象是CompoundRoot。
CompoundRoot繼承了ArraryList,提供了額外的方法:push()和pop()方法,
用來對root對象中所包含的數據進行存取。
正是通過這兩個方法,CompoundRoot變成了一個棧結構.
壓棧操作,將導致對象被放到CompoundRoot的第0個元素上(第0個元素是棧頂),其它對象被依次往後移動;
出棧操作,將導致CompoundRoot的第0個元素被移除(即棧頂元素被彈出),其它對象被依次往前移動
OGNL只能訪問被壓入堆棧(CompoundRoot)中的元素。
在Struts2中,一個請求在最終到達Action的方法之前,Action對象本身會被壓入
ValueStack的CompoundRoot對象中。
所以Action對象是CompoundRoot對象中的一個元素,可以使用OGNL表達式直接訪問。
在jSP頁面中,使用<s:property value=”ognl表達式”/>標籤,將CompoundRoot棧中的值取出。
在<s:property>標籤中的OGNL表達式,最終會交給ValueStack來解釋。
比如:"username"就是一個OGNL表達式,意思是調用root對象的getUsername()方法。
Context
由於在OGNL中,不支持多個root對象,但在struts2的OGNL的表達式中,
有可能需要訪問到多個毫不相干的對象。這時候,我們把表達式中需要用到的對象放到Map中,
傳遞給OGNL,進行訪問。這個Map對象,稱爲context。
可見valueStack對OGNL進行了擴展,使OGNL表達式可以訪問到多個root對象。
要在表達式中訪問到context中的對象,需要使用“#對象名稱”的語法規則。
#表示將context對象的元素放入到CompoundRoot對象的棧頂。訪問完後,
context對象的元素會從棧頂移出,自動釋放。
當CompoundRoot棧中存在多個不相干的root對象時,使用 #root[n]. 進行訪問,
n是root對象當前在棧中的順序。從0開始。
請看下面的例子:
- public class UserAction {
- private String username;
- //查看用戶的詳細信息
- public String detail(){
- username = "張三";
- List list = new ArrayList();
- for(int i=0; i<10; i++){
- User user = new User();
- user.setUsername("User"+i);
- list.add(user);
- }
- ActionContext.getContext().put("users", list);
- User u = new User();
- u.setUsername("趙毅");
- ActionContext.getContext().getValueStack().push(u);
- return "detail";
- }
對應的JSP如下:
- <s:property value="username"/> <br/>
- <s:iterator value="#users">
- <s:property value="username"/>
- <s:property value="#root[2].username"/><br/>
- </s:iterator>
- <s:property value="username"/>
- <s:property value="#root[1].username"/> <!-- 張三 -->
根據剛纔的示例,我們知道,第1行的username是“趙毅”(因爲JSP在執行這行代碼的時候,CompoundRoot中有兩個元素:第0個是“user對象趙毅”,第1個是“userAction對象張三”),因此第1行的username將取出CompoundRoot中第0個元素的username屬性:趙毅
第2行代碼是iterator標籤,只定義了一個value屬性,iterator標籤將循環訪問users這個List中的User對象,並把當前循環的user對象壓入到CompoundRoot中!所以,在第3行和第4行代碼被執行的時候,CompoundRoot中總共有3個元素:第0個元素是被iterator標籤壓入的當前循環的user對象;第1個元素是“user對象趙毅”;第2個元素是“userAction對象張三”,因此第3行代碼的執行結果就是輸出“UserX”,即當前循環的user對象的username屬性!iterator標籤將會依次取出List中的user對象,並不斷壓入/彈出user對象(每次循環,都將執行一遍壓入/彈出)。而第4行代碼取第2個元素的username屬性,即userAction對象的username屬性:張三。
第5行代碼執行完成之後,在CompoundRoot中將剩下2個元素,與第2行代碼被執行之前一樣。所以,第6行代碼的輸出和第1行代碼的輸出結果是一樣的,而第7行代碼將取出userAction對象的username屬性:張三