struts2中OGNL和 ValueStack(一)

學習的時候,總分不清楚在struts2中頁面的傳值和取值是怎麼來完成的,所以從網上搜了很多資料,現在把這些資料總結寫,留着以後參考。。看完資料後也大概明白了。。。

 

先分清楚下ActionContext 、ValueStack 、Stack Context三者

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 棧上下文 也是用來存值的

 

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類進行測試!)
    Action的實例,總是放到value stack中。因爲Action放在stack中,而stack是root(根對象),所以對Action中的屬性的訪問就可以省略#標記。但是,要訪問ActionContext中其它對象的屬性,就必須要帶上#標記,以便讓OGNL知道,不是從根對象,而是從其它對象中去尋找。
    那麼訪問Action中的屬性的代碼就可以這樣寫

<s:property value="postalCode"/>
    其它ActionContext中的非根對象屬性的訪問要像下面這樣寫:
<s:property value="#session.mySessionPropKey"/> or
<s:property value="#session['mySessionPropKey']"/> or
<s:property value="#request['myRequestPropKey']"/>

    對Collection的處理,內容就很簡單。
<s:select label="label" name="name" list="{'name1','name2','name3'}" value="%{'name2'}" />
    這是處理List。這個代碼在頁面上建立一個下拉選項,內容是list中的內容,默認值是name2.
處理map

 <s:select label="label" name="name" list="#{'foo':'foovalue', 'bar':'barvalue'}" />

     需要注意的是,判斷一個值是否在collection中。我們要使用in或者not in來處理。
<s:if test="'foo' in {'foo','bar'}">
   muhahaha
</s:if>
<s:else>
   boo
</s:else>

 另外,可以使用通配符來選擇collection對象的子集。
 ?——所有匹配選擇邏輯的元素
 ^——只提取符合選擇邏輯的第一個元素
 $——只提取符合選擇邏輯的最後一個元素
person.relatives.{? #this.gender == 'male'}

````````````````````````````````````````````````````````````````````````````````` 

 以下爲補充摘錄的一些問題:

提問:在Struts2中,如何使用自身的Tag讀取Action中的變量?

Struts2自身的Tag會根據value中的OGNL表達式,在ValueStack中尋找相應的對象。因爲action在ValueStack的頂部,所以默認情況下,Struts2的Tag中的OGNL表達式將查找action中的變量。請注意,value中的內容直接是OGNL表達式,無需任何el的標籤包裝。

例如:<s:property value="user.name" />

提問:在Struts2中,如何使用自身的Tag讀取HttpServletRequest,HttpSession中的變量?

在上面的知識中,我們知道,Struts2中OGNL的上下文環境中,包含request,session,application等servlet對象的Map封裝。既然這些對象都在OGNL的上下文中,那麼根據OGNL的基本知識,我們可以通過在表達式前面加上#符號來對這些變量的值進行訪問。

例如:<s:property value="%{#application.myApplicationAttribute}" />
<s:property value="%{#session.mySessionAttribute}" />
<s:property value="%{#request.myRequestAttribute}" />
<s:property value="%{#parameters.myParameter}" />

 

提問:在Struts2中,如何使用JSTL來讀取Action中的變量?

這是一個歷史悠久的問題。因爲事實上,很多朋友(包括我在內)是不使用Struts2自身的標籤庫,而是使用JSTL的,可能因爲JSTL標籤庫比較少,簡單易用的原因吧。

我們知道,JSTL默認是從page,request,session,application這四個Scope逐次查找相應的EL表達式所對應的對象的值。那麼如果要使用JSTL來讀取Action中的變量,就需要把Action中的變量,放到request域中才行。所以,早在Webwork2.1.X的年代,我們會編寫一個攔截器來做這個事情的。大致的原理是:在Action執行完返回之前,依次讀取Action中的所有的變量,並依次調用request.setAttribute()來進行設置。具體的整合方式,請參考以下這篇文檔:http://wiki.opensymphony.com/display/WW/Using+WebWork+and+XWork+with+JSP+2.0+and+JSTL+1.1

不過隨着時代的發展,上面的這種方式,已經不再被推薦使用了。(雖然如此,我們依然可以學習它的一個解決問題的思路)目前來說,自從Webwork2.2以後,包括Struts2,都使用另外一種整合方式:對HttpServletRequest進行裝飾。讓我們來看一下源碼:

 

Java代碼 複製代碼
  1. public class StrutsRequestWrapper extends HttpServletRequestWrapper {   
  2.   
  3.     /**  
  4.      * The constructor  
  5.      * @param req The request  
  6.      */  
  7.     public StrutsRequestWrapper(HttpServletRequest req) {   
  8.         super(req);   
  9.     }   
  10.   
  11.     /**  
  12.      * Gets the object, looking in the value stack if not found  
  13.      *  
  14.      * @param s The attribute key  
  15.      */  
  16.     public Object getAttribute(String s) {   
  17.         if (s != null && s.startsWith("javax.servlet")) {   
  18.             // don't bother with the standard javax.servlet attributes, we can short-circuit this   
  19.             // see WW-953 and the forums post linked in that issue for more info   
  20.             return super.getAttribute(s);   
  21.         }   
  22.   
  23.         ActionContext ctx = ActionContext.getContext();   
  24.         Object attribute = super.getAttribute(s);   
  25.   
  26.         boolean alreadyIn = false;   
  27.         Boolean b = (Boolean) ctx.get("__requestWrapper.getAttribute");   
  28.         if (b != null) {   
  29.             alreadyIn = b.booleanValue();   
  30.         }   
  31.   
  32.         // note: we don't let # come through or else a request for   
  33.         // #attr.foo or #request.foo could cause an endless loop   
  34.         if (!alreadyIn && attribute == null && s.indexOf("#") == -1) {   
  35.             try {   
  36.                 // If not found, then try the ValueStack   
  37.                 ctx.put("__requestWrapper.getAttribute", Boolean.TRUE);   
  38.                 ValueStack stack = ctx.getValueStack();   
  39.                 if (stack != null) {   
  40.                     attribute = stack.findValue(s);   
  41.                 }   
  42.             } finally {   
  43.                 ctx.put("__requestWrapper.getAttribute", Boolean.FALSE);   
  44.             }   
  45.         }   
  46.         return attribute;   
  47.     }   
  48. }  
  1. public class StrutsRequestWrapper extends HttpServletRequestWrapper {  
  2.   
  3.     /** 
  4.      * The constructor 
  5.      * @param req The request 
  6.      */  
  7.     public StrutsRequestWrapper(HttpServletRequest req) {  
  8.         super(req);  
  9.     }  
  10.   
  11.     /** 
  12.      * Gets the object, looking in the value stack if not found 
  13.      * 
  14.      * @param s The attribute key 
  15.      */  
  16.     public Object getAttribute(String s) {  
  17.         if (s != null && s.startsWith("javax.servlet")) {  
  18.             // don't bother with the standard javax.servlet attributes, we can short-circuit this  
  19.             // see WW-953 and the forums post linked in that issue for more info  
  20.             return super.getAttribute(s);  
  21.         }  
  22.   
  23.         ActionContext ctx = ActionContext.getContext();  
  24.         Object attribute = super.getAttribute(s);  
  25.   
  26.         boolean alreadyIn = false;  
  27.         Boolean b = (Boolean) ctx.get("__requestWrapper.getAttribute");  
  28.         if (b != null) {  
  29.             alreadyIn = b.booleanValue();  
  30.         }  
  31.   
  32.         // note: we don't let # come through or else a request for  
  33.         // #attr.foo or #request.foo could cause an endless loop  
  34.         if (!alreadyIn && attribute == null && s.indexOf("#") == -1) {  
  35.             try {  
  36.                 // If not found, then try the ValueStack  
  37.                 ctx.put("__requestWrapper.getAttribute", Boolean.TRUE);  
  38.                 ValueStack stack = ctx.getValueStack();  
  39.                 if (stack != null) {  
  40.                     attribute = stack.findValue(s);  
  41.                 }  
  42.             } finally {  
  43.                 ctx.put("__requestWrapper.getAttribute", Boolean.FALSE);  
  44.             }  
  45.         }  
  46.         return attribute;  
  47.     }  
  48. }  

 看到了嘛?這個類會Struts2初始化的時候,替換HttpServletRequest,運行於整個Struts2的運行過程中,當我們試圖調用request.getAttribute()的時候,就會執行上面的這個方法。(這是一個典型的裝飾器模式)在執行上面的方法時,會首先調用HttpServletRequest中原本的request.getAttribute(),如果沒有找到,它會繼續到ValueStack中去查找,而action在ValueStack中,所以action中的變量通過OGNL表達式,就能找到對應的值了。

在這裏,在el表達式廣泛使用的今天,JSTL1.1以後,也支持直接使用el表達式。注意與直接使用struts2的tag的區別,這裏需要使用el的表示符號:${}

例如:${user.name}, <c:out value="${department.name}" />

提問:在Struts2中,如何使用Freemarker等模板來讀取Action中的變量以及HttpServletRequest和HttpSession中的變量?

Freemarker等模板在Struts2中有對應的Result,而在這些Result中,Freemarker等模板會根據ValueStack和ActionContext中的內容,構造這些模板可識別的Model,從而使得模板可以以他們各自的語法對ValueStack和ActionContext中的內容進行讀取。

有關Freemarker對於變量的讀取,可以參考Struts2的官方文檔,非常詳細:http://struts.apache.org/2.0.14/docs/freemarker.html

設值計算

Struts2中使用OGNL進行設值計算,就是指View層傳遞數據到Control層,並且能夠設置到相應的Java對象中。這個過程從邏輯上說需要分成兩步來完成:

1. 對於每個請求,都建立一個與相應Action對應的ActionContext作爲OGNL的上下文環境和ValueStack,並且把Action壓入ValueStack

2. 在請求進入Action代碼前,通過某種通用的機制,蒐集頁面上傳遞過來的參數,並調用OGNL相關的代碼,對Action進行設值。
上面的第一個步驟,在處理URL請求時完成,而第二個步驟由struts2內置的攔截器完成。

 

 

由於瀏覽了過多的文章,,具體的源地址未能記錄。。但還是感謝網上的原作者。。。

補充摘錄的網址:http://www.javaeye.com/wiki/struts2/1356-how-to-use-ognl-in-struts2

轉載地址:http://blog.csdn.net/siobhan/article/details/4378434



發佈了13 篇原創文章 · 獲贊 1 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章