在Struts2中使用ValueStack、ActionContext、ServletContext、request、session等 .

聲明:本文參考Struts2版本爲2.3.1.2,內容僅供參考,限於筆者水平有限,不免有所疏漏,望您能友善指出。本文發表於ITEYE,謝絕轉載。css

1. ValueStack

    ValueStack在中文版的《Struts2深刻淺出》一書中譯做「值棧」。其自己數據結構是一個棧,使用者能夠把一些對象(又稱做bean)存入值棧中,而後使用動態的表達式來讀取bean的屬性,或者對bean進行一些其餘操做。因爲值棧中可能有多個bean,值棧會按bean出棧的順序依次嘗試使用動態的表達式來讀取值,直到成功讀取值爲止。在Struts2中,默認的值棧實現是OgnlValueStack,即默認使用Ognl這個動態表達式語言來讀取值。html

    在Struts2執行一次請求的過程當中,Struts2會把當前的Action對象自動放入值棧。這樣,在渲染JSP時,JSP裏的代碼使用<s:property value="..."/>之類標籤中的Ognl表達式會直接做用於Action對象,從而方便的讀取Action的屬性。java

     如何獲得值棧:
  • 在自定義的攔截器中,使用ActionInvocation.getStack()方法( ActionInvocation 是攔截器的方法參數)。
  • 在Action類中,讓攔截器注入ValueStack或者使用ActionContext.getContext().getValueStack()來值棧(ActionContext.getContext()爲靜態方法)。注意:ActionContext分配context的方式是基於線程的,若是使用這種方法,請確保它不會出錯。
  • 在JSP中,直接使用標籤便可得到值棧裏的數據,而通常不用獲取值棧自己。
    如何將對象存入值棧:
  • Struts2自動存入Action:以前已經提到,Struts2在執行一次請求的過程當中會把當前的Action對象自動存入值棧中。
  • ModelDrivenInterceptor會存入Action的model屬性:若是你使用了Struts2提供的 ModelDrivenInterceptor,則它會把Action對象的getModel()方法獲得的對象存入值棧中。此時,值棧最底層爲Action類,其次爲這個model。
  • 在自定義的攔截器中存入值棧:獲得值棧對象後調用ValueStack.put(Object object)方法。
  • 在Action類中存入值棧:獲得值棧對象後調用ValueStack.put(Object object)方法。
  • 在JSP中存入值棧:標籤<s:push value="..."></s:push>是專門用來在JSP中把指定的value放入值棧的,但value被放入值棧的時間僅在s:push標籤內,即程序運行到</s:push>標籤處會把value從值棧中移出。另外,還有一些標籤好比<s:iterator/>因爲其功能的須要也會把一些對象放到值棧中。
    讓值棧執行表達式來得到值:
  • 在自定義的攔截器中,得到值棧後,使用ValueStack.findValue(...)等方法。
  • 在Action類中,得到值棧後,使用ValueStack.findVlaue(...)等方法。
  • 在JSP中,一些標籤的屬性是直接在值棧上執行Ognl表達式的,好比<s:property/>的value屬性。若是標籤的屬性不是直接執行Ognl表達式的,則須要使用「%{}」將表達式括起來,這樣Struts2就會以Ognl表達式來執行了。至於到底哪些標籤是直接執行Ognl而哪些不是,請參考完整的官方文檔
    在JSP中跳過棧頂元素直接訪問第二層:
  • 在JSP中,使用[0]、[1]等表達式來指定從棧的第幾層開始執行表達式。[0]表示從棧頂開始,[1]表示從棧的第二層開始。好比表達式「name」等價於「[0].name」。參見此處。
    在JSP中訪問值棧對象自己(而不是它們的屬性)
  • 在表示式中使用top關鍵字來訪問對象自己。好比,表達式「name」等價於「top.name」,表達式「[0].top」等價於「top」,表達式「[1].top.name」等價於「[1].name」。

    總之,值棧主要目的是爲了讓JSP內能方便的訪問Action的屬性。apache

 
    一些例子:
 
Java代碼  複製代碼  收藏代碼
  1. // 此類爲一個封裝數據的簡單類,在下面的例子會用到   
  2. public class Person {   
  3.   
  4.     private String name;   
  5.   
  6.     public String getName() {   
  7.         return name;   
  8.     }   
  9.   
  10.     public void setName(String name) {   
  11.         this.name = name;   
  12.     }   
  13. }  
[java]  view plain copy
  1. // 此類爲一個封裝數據的簡單類,在下面的例子會用到  
  2. public class Person {  
  3.   
  4.     private String name;  
  5.   
  6.     public String getName() {  
  7.         return name;  
  8.     }  
  9.   
  10.     public void setName(String name) {  
  11.         this.name = name;  
  12.     }  
  13. }  
 
 
Java代碼  複製代碼  收藏代碼
  1. // 本類將演示攔截器中對值棧的操做   
  2. public class MyInterceptor extends AbstractInterceptor {   
  3.   
  4.     public String intercept(ActionInvocation invocation) throws Exception {   
  5.         // 得到值棧   
  6.         ValueStack valueStack = invocation.getStack();   
  7.         // 存入值   
  8.         Person person = new Person();   
  9.         valueStack.push(person);   
  10.         // 執行表達式獲取值   
  11.         String name = (String) valueStack.findValue("name");   
  12.         // 其餘代碼   
  13.         return invocation.invoke();   
  14.     }   
  15. }  
[java]  view plain copy
  1. // 本類將演示攔截器中對值棧的操做  
  2. public class MyInterceptor extends AbstractInterceptor {  
  3.   
  4.     public String intercept(ActionInvocation invocation) throws Exception {  
  5.         // 得到值棧  
  6.         ValueStack valueStack = invocation.getStack();  
  7.         // 存入值  
  8.         Person person = new Person();  
  9.         valueStack.push(person);  
  10.         // 執行表達式獲取值  
  11.         String name = (String) valueStack.findValue("name");  
  12.         // 其餘代碼  
  13.         return invocation.invoke();  
  14.     }  
  15. }  
 
Java代碼  複製代碼  收藏代碼
  1. // 本類將演示在Action中對值棧進行操做   
  2. public class MyAction extends ActionSupport {   
  3.   
  4.     @Override  
  5.     public String execute() throws Exception {   
  6.         // 得到值棧   
  7.         ValueStack valueStack = ActionContext.getContext().getValueStack();   
  8.         // 存入值   
  9.         Person person = new Person();// 這是以前例子中定義的類  
  10.         valueStack.push(person);   
  11.         // 執行表達式獲取值   
  12.         String name = (String) valueStack.findValue("name");   
  13.         // 其餘代碼   
  14.         // ......   
  15.         return SUCCESS;   
  16.     }   
  17.     // 如下定義的屬性供接下來的JSP例子使用   
  18.     private String message;   
  19.     private Person person;   
  20.     private List<Person> personList;   
  21.   
  22.     public String getMessage() {   
  23.         return message;   
  24.     }   
  25.   
  26.     public Person getPerson() {   
  27.         return person;   
  28.     }   
  29.   
  30.     public List<Person> getPersonList() {   
  31.         return personList;   
  32.     }   
  33. }  
[java]  view plain copy
  1. // 本類將演示在Action中對值棧進行操做  
  2. public class MyAction extends ActionSupport {  
  3.   
  4.     @Override  
  5.     public String execute() throws Exception {  
  6.         // 得到值棧  
  7.         ValueStack valueStack = ActionContext.getContext().getValueStack();  
  8.         // 存入值  
  9.         Person person = new Person();// 這是以前例子中定義的類  
  10.         valueStack.push(person);  
  11.         // 執行表達式獲取值  
  12.         String name = (String) valueStack.findValue("name");  
  13.         // 其餘代碼  
  14.         // ......  
  15.         return SUCCESS;  
  16.     }  
  17.     // 如下定義的屬性供接下來的JSP例子使用  
  18.     private String message;  
  19.     private Person person;  
  20.     private List<Person> personList;  
  21.   
  22.     public String getMessage() {  
  23.         return message;  
  24.     }  
  25.   
  26.     public Person getPerson() {  
  27.         return person;  
  28.     }  
  29.   
  30.     public List<Person> getPersonList() {  
  31.         return personList;  
  32.     }  
  33. }  
 
  
Html代碼  複製代碼  收藏代碼
  1. <%@page contentType="text/html" pageEncoding="UTF-8"%>  
  2. <%@taglib uri="/struts-tags" prefix="s" %>  
  3. <!DOCTYPE html>  
  4. <html>  
  5.     <head>  
  6.         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
  7.         <title>JSP Page</title>  
  8.     </head>  
  9.     <body>  
  10.         <!-- 本JSP將演示在JSP中對值棧的使用 -->  
  11.         <!-- 本JSP爲MyAction對應的JSP -->  
  12.   
  13.         <!-- 因爲Action已經被存入的值棧,因此能夠調用Action的屬性  -->  
  14.         <!-- 使用下面的標籤和表達式來顯示MyAction的message屬性 -->  
  15.         <s:property value="message"/>  
  16.         <!-- 使用下面的標籤和表達式來調用Action的getText(...)方法,參數爲MyAction的message屬性  -->  
  17.         <s:property value="getText(message)"/>  
  18.         <!-- 默認狀況下傳遞給cssClass的是字符串常量。可使用「%{}」來啓用Ognl,這樣,傳遞給cssClass的就不是字符串常量"message",而是上面所說的message的值  -->  
  19.         <s:div cssClass="%{message}"/>  
  20.         <!-- 使用s:push標籤來將對象放入值棧,以下 -->  
  21.         <s:push value="person">  
  22.             <!-- 在此s:push標籤內,值棧的棧頂元素爲person,棧頂第二層爲action   
  23.             <!-- 在標籤內直接調用person的屬性(而不是Action的屬性),以下 -->  
  24.             <s:property value="name"/>  
  25.             <!-- 在標籤內也可使用MyAction的屬性,值棧會依次先查找Person是否有該屬性,因爲沒找到,會再MyAction中再查找,以下  -->  
  26.             <s:property value="message"/>  
  27.             <!-- 可使用「[0]」、「[1]」等指定從值棧的哪一層開始查找  -->  
  28.             <!-- 此時,使用「[0]」表示從Person開始查找,固然仍是找不到,值棧就接着到MyAction中查找,以下  -->  
  29.             <s:property value="[0].message"/>  
  30.             <!-- 此時,使用「[1]」將從MyAction開始查找,而跳過了person,以下  -->  
  31.             <s:property value="[1].message"/>  
  32.             <!-- 想要訪問棧頂元素自己使用關鍵字「top」,好比,下面的top就表明棧頂的person,以下 -->  
  33.             <s:property value="top"/>  
  34.             <!-- 或者以下  -->  
  35.             <s:property value="[0].top"/>  
  36.             <!-- 想要訪問MyAction自己的話使用以下寫法  -->  
  37.             <s:property value="[1].top"/>  
  38.         </s:push>  
  39.         <!-- 此時person已被移出值棧,再使用以下標籤和表達式將沒法獲得值 -->  
  40.         <!--<s:property value="name"/>-->  
  41.         <!-- iterator標籤會把list的每一個元素依次存入棧頂,以下 -->  
  42.         <s:iterator value="personList">  
  43.             <!-- 得到List每一個元素中的name屬性  -->  
  44.             <s:property value="name"/>  
  45.         </s:iterator>  
  46.     </body>  
  47. </html>  
[html]  view plain copy
  1. <%@page contentType="text/html" pageEncoding="UTF-8"%>  
  2. <%@taglib uri="/struts-tags" prefix="s" %>  
  3. <!DOCTYPE html>  
  4. <html>  
  5.     <head>  
  6.         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
  7.         <title>JSP Page</title>  
  8.     </head>  
  9.     <body>  
  10.         <!-- 本JSP將演示在JSP中對值棧的使用 -->  
  11.         <!-- 本JSP爲MyAction對應的JSP -->  
  12.   
  13.         <!-- 因爲Action已經被存入的值棧,因此能夠調用Action的屬性  -->  
  14.         <!-- 使用下面的標籤和表達式來顯示MyAction的message屬性 -->  
  15.         <s:property value="message"/>  
  16.         <!-- 使用下面的標籤和表達式來調用Action的getText(...)方法,參數爲MyAction的message屬性  -->  
  17.         <s:property value="getText(message)"/>  
  18.         <!-- 默認狀況下傳遞給cssClass的是字符串常量。可使用「%{}」來啓用Ognl,這樣,傳遞給cssClass的就不是字符串常量"message",而是上面所說的message的值  -->  
  19.         <s:div cssClass="%{message}"/>  
  20.         <!-- 使用s:push標籤來將對象放入值棧,以下 -->  
  21.         <s:push value="person">  
  22.             <!-- 在此s:push標籤內,值棧的棧頂元素爲person,棧頂第二層爲action  
  23.             <!-- 在標籤內直接調用person的屬性(而不是Action的屬性),以下 -->  
  24.             <s:property value="name"/>  
  25.             <!-- 在標籤內也可使用MyAction的屬性,值棧會依次先查找Person是否有該屬性,因爲沒找到,會再MyAction中再查找,以下  -->  
  26.             <s:property value="message"/>  
  27.             <!-- 可使用「[0]」、「[1]」等指定從值棧的哪一層開始查找  -->  
  28.             <!-- 此時,使用「[0]」表示從Person開始查找,固然仍是找不到,值棧就接着到MyAction中查找,以下  -->  
  29.             <s:property value="[0].message"/>  
  30.             <!-- 此時,使用「[1]」將從MyAction開始查找,而跳過了person,以下  -->  
  31.             <s:property value="[1].message"/>  
  32.             <!-- 想要訪問棧頂元素自己使用關鍵字「top」,好比,下面的top就表明棧頂的person,以下 -->  
  33.             <s:property value="top"/>  
  34.             <!-- 或者以下  -->  
  35.             <s:property value="[0].top"/>  
  36.             <!-- 想要訪問MyAction自己的話使用以下寫法  -->  
  37.             <s:property value="[1].top"/>  
  38.         </s:push>  
  39.         <!-- 此時person已被移出值棧,再使用以下標籤和表達式將沒法獲得值 -->  
  40.         <!--<s:property value="name"/>-->  
  41.         <!-- iterator標籤會把list的每一個元素依次存入棧頂,以下 -->  
  42.         <s:iterator value="personList">  
  43.             <!-- 得到List每一個元素中的name屬性  -->  
  44.             <s:property value="name"/>  
  45.         </s:iterator>  
  46.     </body>  
  47. </html>  
 

2.ActionContext

    ActionContext是Action的上下文,Struts2自動在其中保存了一些在Action執行過程當中所需的對象,好比session, parameters, locale等。Struts2會根據每一個執行HTTP請求的線程來建立對應的ActionContext,即一個線程有一個惟一的ActionContext。所以,使用者可使用靜態方法ActionContext.getContext()來獲取當前線程的ActionContext,也正是因爲這個緣由,使用者不用去操心讓Action是線程安全的。api

    不管如何,ActionContext都是用來存放數據的。Struts2自己會在其中放入很多數據,而使用者也能夠放入本身想要的數據。ActionContext自己的數據結構是映射結構,即一個Map,用key來映射value。因此使用者徹底能夠像使用Map同樣來使用它,或者直接使用Action.getContextMap()方法來對Map進行操做。瀏覽器

    Struts2自己在其中放入的數據有ActionInvocation、application(即ServletContext)、conversionErrors、Locale、action的name、request的參數、HTTP的Session以及值棧等。完整的列表請參考它的Javadoc(本文附錄有對它包含內容的討論)。安全

    因爲ActionContext的線程惟一和靜態方法就能得到的特性,使得在非Action類中能夠直接得到它,而不須要等待Action傳入或注入。須要注意的是,它僅在因爲request而建立的線程中有效(由於request時才建立對應的ActionContext),而在服務器啓動的線程中(好比fliter的init方法)無效。因爲在非Action類中訪問其的方便性,ActionContext也能夠用來在非Action類中向JSP傳遞數據(由於JSP也能很方便的訪問它)。服務器

 

   ValueStack與ActionContext的聯繫和區別:
  • 相同點:它們都是在一次HTTP請求的範圍內使用的,即它們的生命週期都是一次請求。
  • 不一樣點:值棧是棧的結構,ActionContext是映射(Map)的結構。
  • 聯繫:ValueStack.getContext()方法獲得的Map其實就是ActionContext的Map。查看Struts2的源代碼可知(Struts2.3.1.2的org.apache.struts2.dispatcher.ng.PrepareOperations的第79行,createActionContext方法),在建立ActionContext時,就是把ValueStack.getContext()做爲ActionContext的構造函數的參數。因此,ValueStack和ActionContext本質上能夠互相得到。
  • 注意:在一些文檔中,會出現把對象存入「stack's context」的字樣,其實就是把值存入了ActionContext。因此在閱讀這些文檔時,要看清楚,究竟是放入了棧結構(即值棧),仍是映射結構(值棧的context,即ActionContext)。
    如何得到ActionContext:
  • 在自定義的攔截器中:使用ActionInvocation.getInvocationContext()或者使用ActionContext.getContext()。
  • 在Action類中:讓攔截器注入或者使用ActionContext.getContext()。
  • 在非Action類中:讓Action類傳遞參數、使用注入機制注入或者使用ActionContext.getContext()。注意:只有運行在request線程中的代碼才能調用ActionContext.getContext(),不然返回的是null。
  • 在JSP中:通常不須要得到ActionContext自己。
    如何向ActionContext中存入值:
  • 在攔截器、Action類、非Action類等Java類中:使用ActionContext.put(Object key, Object value)方法。
  • 在JSP中:標籤<s:set value="..."/>默認將值存入ActionContext中(固然,<s:set>標籤還能夠把值存到其餘地方)。另外,許多標籤都有var屬性(之前用的是id屬性,如今id屬性已被棄用),這個屬性能向ActionContext存入值,key爲var屬性的值,value爲標籤的value屬性的值。(有些文檔寫的是向ValueStack的context存入值,實際上是同樣的)
    如何從ActionContext中讀取值:
  • 在攔截器、Action類、非Action類等Java類中:使用ActionContext.get(Object key)方法。
  • 在JSP中:使用#開頭的Ognl表達式,好比<s:property value="#name"/>會調用ActionContext.get("name")方法。注意:若是某標籤的屬性默認不做爲Ognl表達式解析,則須要使用%{}把表達式括起來,因而就會出現相似「%{#name}的表達式」。(「#」的更多用途參見這裏
    總之,在JSP中使用ActionContext一方面是因爲它是映射結構,另外一方面是能讀取Action的一些配置。當你須要爲許多Action提供通用的值的話,可讓每一個Action都提供getXXX()方法,但更好的方法是在攔截器或JSP模板中把這些通用的值存放到ActionContext中(由於攔截器或JSP模板每每通用於多個Action)。
 
    一些例子:
Java代碼  複製代碼  收藏代碼
  1. // 本類將演示攔截器中對ActionContext的操做   
  2. public class MyInterceptor extends AbstractInterceptor {   
  3.   
  4.     public String intercept(ActionInvocation invocation) throws Exception {   
  5.         // 得到ActionContext   
  6.         ActionContext actionContext = invocation.getInvocationContext();   
  7.         // 存入值   
  8.         Person person = new Person();   
  9.         actionContext.put("person", person);   
  10.         // 獲取值   
  11.         Object value = actionContext.get("person");   
  12.         // 獲取HttpServletRequest   
  13.         HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);   
  14.         // 獲取request的Map,即HttpServletRequest.getAttribute(...)和HttpServletRequest.setAttribute(...)所操做的值  
  15.         Map requestMap = (Map) actionContext.get("request");   
  16.         // 其餘代碼   
  17.         // ......   
  18.         return invocation.invoke();   
  19.     }   
  20. }  
[java]  view plain copy
  1. // 本類將演示攔截器中對ActionContext的操做  
  2. public class MyInterceptor extends AbstractInterceptor {  
  3.   
  4.     public String intercept(ActionInvocation invocation) throws Exception {  
  5.         // 得到ActionContext  
  6.         ActionContext actionContext = invocation.getInvocationContext();  
  7.         // 存入值  
  8.         Person person = new Person();  
  9.         actionContext.put("person", person);  
  10.         // 獲取值  
  11.         Object value = actionContext.get("person");  
  12.         // 獲取HttpServletRequest  
  13.         HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);  
  14.         // 獲取request的Map,即HttpServletRequest.getAttribute(...)和HttpServletRequest.setAttribute(...)所操做的值  
  15.         Map requestMap = (Map) actionContext.get("request");  
  16.         // 其餘代碼  
  17.         // ......  
  18.         return invocation.invoke();  
  19.     }  
  20. }  
 
Java代碼  複製代碼  收藏代碼
  1. // 本類將演示在Action中對ActionContext進行操做   
  2. public class MyAction extends ActionSupport {   
  3.   
  4.     @Override  
  5.     public String execute() throws Exception {   
  6.         // 得到值棧   
  7.         ActionContext actionContext = ActionContext.getContext();   
  8.         // 存入值   
  9.         Person person = new Person();// 這是以前例子中定義的類  
  10.         actionContext.put("person", person);   
  11.         // 獲取值   
  12.         Object object = actionContext.get("person");   
  13.         // 其餘代碼   
  14.         // ......   
  15.         return SUCCESS;   
  16.     }   
  17. }  
[java]  view plain copy
  1. // 本類將演示在Action中對ActionContext進行操做  
  2. public class MyAction extends ActionSupport {  
  3.   
  4.     @Override  
  5.     public String execute() throws Exception {  
  6.         // 得到值棧  
  7.         ActionContext actionContext = ActionContext.getContext();  
  8.         // 存入值  
  9.         Person person = new Person();// 這是以前例子中定義的類  
  10.         actionContext.put("person", person);  
  11.         // 獲取值  
  12.         Object object = actionContext.get("person");  
  13.         // 其餘代碼  
  14.         // ......  
  15.         return SUCCESS;  
  16.     }  
  17. }  
 
Html代碼  複製代碼  收藏代碼
  1. <!DOCTYPE html>  
  2. <html>  
  3.     <head>  
  4.         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
  5.         <title>JSP Page</title>  
  6.     </head>  
  7.     <body>  
  8.         <!-- 本JSP將演示在JSP中對ActionContext的使用 -->  
  9.         <!-- 本JSP爲MyAction對應的JSP -->  
  10.   
  11.         <!-- 因爲Action中已經向ActionContext存入了key爲"person"的值,因此可使用「#person」來獲取它,以下  -->  
  12.         <s:property value="#person"/>  
  13.         <!-- 得到person的name屬性,以下  -->  
  14.         <s:property value="#person.name"/>  
  15.         <!-- 得到Struts2在ActionContext中存入的值,好比request的Map,以下  -->  
  16.         <s:property value="#request"/>  
  17.         <!-- 得到Struts2在ActionContext中存入的值,好比session的Map,以下  -->  
  18.         <s:property value="#session"/>  
  19.         <!-- 得到Struts2在ActionContext中存入的值,request請求傳遞的GET參數或POST參數的Map,以下  -->  
  20.         <s:property value="#parameters"/>  
  21.            
  22.         <!-- 如下演示在JSP中把值存入ActionContext中  -->  
  23.         <!-- 存入一個字符串"myName",key爲"myKey",以下 -->  
  24.         <s:set value="%{'myName'}" var="myKey"/>  
  25.         <!-- 使用s:bean標籤來建立一個對象,並把它存入ActionContext中,key爲myObject,以下  -->  
  26.         <s:bean name="com.example.Person" var="myObject"/>  
  27.         <!-- 以後就能夠用「#」來讀取它們,以下  -->  
  28.         <s:property value="#myKey"/>  
  29.         <s:property value="#myObject"/>  
  30.     </body>  
  31. </html>  
[html]  view plain copy
  1. <!DOCTYPE html>  
  2. <html>  
  3.     <head>  
  4.         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
  5.         <title>JSP Page</title>  
  6.     </head>  
  7.     <body>  
  8.         <!-- 本JSP將演示在JSP中對ActionContext的使用 -->  
  9.         <!-- 本JSP爲MyAction對應的JSP -->  
  10.   
  11.         <!-- 因爲Action中已經向ActionContext存入了key爲"person"的值,因此可使用「#person」來獲取它,以下  -->  
  12.         <s:property value="#person"/>  
  13.         <!-- 得到person的name屬性,以下  -->  
  14.         <s:property value="#person.name"/>  
  15.         <!-- 得到Struts2在ActionContext中存入的值,好比request的Map,以下  -->  
  16.         <s:property value="#request"/>  
  17.         <!-- 得到Struts2在ActionContext中存入的值,好比session的Map,以下  -->  
  18.         <s:property value="#session"/>  
  19.         <!-- 得到Struts2在ActionContext中存入的值,request請求傳遞的GET參數或POST參數的Map,以下  -->  
  20.         <s:property value="#parameters"/>  
  21.           
  22.         <!-- 如下演示在JSP中把值存入ActionContext中  -->  
  23.         <!-- 存入一個字符串"myName",key爲"myKey",以下 -->  
  24.         <s:set value="%{'myName'}" var="myKey"/>  
  25.         <!-- 使用s:bean標籤來建立一個對象,並把它存入ActionContext中,key爲myObject,以下  -->  
  26.         <s:bean name="com.example.Person" var="myObject"/>  
  27.         <!-- 以後就能夠用「#」來讀取它們,以下  -->  
  28.         <s:property value="#myKey"/>  
  29.         <s:property value="#myObject"/>  
  30.     </body>  
  31. </html>  
 

3. HttpServletRequest類或request的Map

    Struts2中提供了兩種對request的操做:一種是Web服務器提供的HttpServletRequest類,這和傳統Java Web項目中的操做request的方式相同;另外一種是一個「request的Map」,即封裝了HttpServletRequest的attributes的映射類,操做該Map至關於操做HttpServletRequest的attributes。之因此提供了Map的操做方式,一是方便操做,二是能方便使用Ognl在JSP標籤中讀取request。不管如何,這兩個request是互通的。至於request的生命週期等概念,與其餘的Java Web項目沒有區別,本文再也不詳述。session

 

    使用HttpServletRequest類仍是request的Map
  • 雖然二者是互通的,但就讀取request的attributes而言,使用request的Map要方便許多,而且不會暴露沒必要要的接口。固然,HttpServletRequest有一些request的Map沒有的方法,使用這些方法時固然仍是要用前者。
    使用request的Map仍是ActionContext:
  • 二者都是Map,二者的生命週期都是一個請求。
  • 傳統的Java Web項目中,每每是經過request的attributes來向JSP傳遞值的:先在Servlet裏setAttribute(),而後在JSP裏getAttribute()。固然在Struts2的項目中,你仍然可使用這個方法,然而拋棄了Struts2提供的傳遞功能是得不償失的。雖然筆者沒有找到官方文檔說必定要用ActionContext替換request的Map,也沒有發現程序中有能得到ActionContext卻得到不了request的Map的地方,但在Struts2框架下,操做ActionContext要比操做request的Map更加方便。所以,筆者建議:儘可能使用ActionContext而不是request的Map來傳遞值。
  • request的Map有時候會包含其餘框架設置的值,好比Spring框架。獲取這些值的時候就須要用request的Map了,由於ActionContext裏沒有。
  • 經過ActionContext能夠得到HttpServletRequest類:「HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);」。
  • 經過ActionContext也能夠得到request的Map:「Map requestMap = (Map) actionContext.get("request");」。所以,在JSP標籤中,使用表達式「#request」就能夠得到request的Map的數據。
    如何得到HttpServletRequest:
  • 若是已經有ActionContext,則使用「actionContext.get(StrutsStatics.HTTP_REQUEST)」來得到HttpServletRequest。
  • 在自定義的攔截器中,先得到ActionContext,再經過ActionContext來得到。
  • 在Action中,先得到ActionContext,再經過ActionContext來得到。或者讓Action實現ServletRequestAware接口,並使用ServletConfigInterceptor攔截器,這樣這個攔截器就會注入HttpServletRequest。
  • 在JSP中,通常不須要得到HttpServletRequest。
    如何得到request的Map:
  • 若是已經有ActionContext,則使用「actionContext.get("request")」來得到。
  • 在自定義的攔截器中,先得到 ActionContext,再經過ActionContext來得到。
  • 在Action中,先得到ActionContext,再經過ActionContext來得到。或者讓Action實現RequestAware接口,並使用ServletConfigInterceptor攔截器,這樣這個攔截器就會注入Map request。
  • 在JSP中,用「#request」來得到request的Map,用「#request.key」或者「#request['key']」來讀取Map中的值。

    總之,request仍然符合Java Web網站的通常規律。不過筆者建議使用者應儘可能避免用request傳值。數據結構

 

    一些例子:

 

Java代碼  複製代碼  收藏代碼
  1. // 本類將演示攔截器中對HttpServletRequest和request的Map的操做   
  2. public class MyInterceptor extends AbstractInterceptor {   
  3.   
  4.     public String intercept(ActionInvocation invocation) throws Exception {   
  5.         // 得到ActionContext   
  6.         ActionContext actionContext = invocation.getInvocationContext();   
  7.         // 得到HttpServletRequest   
  8.         HttpServletRequest httpServletRequest=(HttpServletRequest)actionContext.get(StrutsStatics.HTTP_REQUEST);   
  9.         // 得到request的Map   
  10.         Map requestMap = (Map) actionContext.get("request");   
  11.         // 建立一個類做爲實例   
  12.         Person person = new Person();   
  13.         // 如下兩行的語句做用相同   
  14.         httpServletRequest.setAttribute("person", person);   
  15.         requestMap.put("person", person);   
  16.         // 其餘代碼   
  17.         // ......   
  18.         return invocation.invoke();   
  19.     }   
  20. }  
[java]  view plain copy
  1. // 本類將演示攔截器中對HttpServletRequest和request的Map的操做  
  2. public class MyInterceptor extends AbstractInterceptor {  
  3.   
  4.     public String intercept(ActionInvocation invocation) throws Exception {  
  5.         // 得到ActionContext  
  6.         ActionContext actionContext = invocation.getInvocationContext();  
  7.         // 得到HttpServletRequest  
  8.         HttpServletRequest httpServletRequest=(HttpServletRequest)actionContext.get(StrutsStatics.HTTP_REQUEST);  
  9.         // 得到request的Map  
  10.         Map requestMap = (Map) actionContext.get("request");  
  11.         // 建立一個類做爲實例  
  12.         Person person = new Person();  
  13.         // 如下兩行的語句做用相同  
  14.         httpServletRequest.setAttribute("person", person);  
  15.         requestMap.put("person", person);  
  16.         // 其餘代碼  
  17.         // ......  
  18.         return invocation.invoke();  
  19.     }  
  20. }  

 

 

 

Java代碼  複製代碼  收藏代碼
  1. // 本類將演示在Action中對HttpServletRequest和request的Map進行操做(靜態方法得到ActionContext)  
  2. public class MyAction extends ActionSupport {   
  3.   
  4.     @Override  
  5.     public String execute() throws Exception {   
  6.         // 得到ActionContext   
  7.         ActionContext actionContext = ActionContext.getContext();   
  8.         // 得到HttpServletRequest   
  9.         HttpServletRequest httpServletRequest=(HttpServletRequest)actionContext.get(StrutsStatics.HTTP_REQUEST);   
  10.         // 得到request的Map   
  11.         Map requestMap = (Map) actionContext.get("request");   
  12.         // 建立一個類做爲實例   
  13.         Person person = new Person();   
  14.         // 如下兩行的語句做用相同   
  15.         httpServletRequest.setAttribute("person", person);   
  16.         requestMap.put("person", person);   
  17.         // 其餘代碼   
  18.         // ......   
  19.         return SUCCESS;   
  20.     }   
  21. }  
[java]  view plain copy
  1. // 本類將演示在Action中對HttpServletRequest和request的Map進行操做(靜態方法得到ActionContext)  
  2. public class MyAction extends ActionSupport {  
  3.   
  4.     @Override  
  5.     public String execute() throws Exception {  
  6.         // 得到ActionContext  
  7.         ActionContext actionContext = ActionContext.getContext();  
  8.         // 得到HttpServletRequest  
  9.         HttpServletRequest httpServletRequest=(HttpServletRequest)actionContext.get(StrutsStatics.HTTP_REQUEST);  
  10.         // 得到request的Map  
  11.         Map requestMap = (Map) actionContext.get("request");  
  12.         // 建立一個類做爲實例  
  13.         Person person = new Person();  
  14.         // 如下兩行的語句做用相同  
  15.         httpServletRequest.setAttribute("person", person);  
  16.         requestMap.put("person", person);  
  17.         // 其餘代碼  
  18.         // ......  
  19.         return SUCCESS;  
  20.     }  
  21. }  

 

 

 

Java代碼  複製代碼  收藏代碼
  1. // 本類將演示在Action中使用ServletRequestAware得到HttpServletRequest(注意:要使用ServletConfigInterceptor攔截器)  
  2. public class MyAction extends ActionSupport implements ServletRequestAware {   
  3.   
  4.     private HttpServletRequest request;   
  5.        
  6.     //此方法是接口ServletRequestAware的方法   
  7.     public void setServletRequest(HttpServletRequest request) {   
  8.         this.request = request;   
  9.     }   
  10.   
  11.     @Override  
  12.     public String execute() throws Exception {   
  13.         // HttpServletRequest已在該類的字段中準備好,可直接使用  
  14.         // ......   
  15.         return SUCCESS;   
  16.     }   
  17. }  
[java]  view plain copy
  1. // 本類將演示在Action中使用ServletRequestAware得到HttpServletRequest(注意:要使用ServletConfigInterceptor攔截器)  
  2. public class MyAction extends ActionSupport implements ServletRequestAware {  
  3.   
  4.     private HttpServletRequest request;  
  5.       
  6.     //此方法是接口ServletRequestAware的方法  
  7.     public void setServletRequest(HttpServletRequest request) {  
  8.         this.request = request;  
  9.     }  
  10.   
  11.     @Override  
  12.     public String execute() throws Exception {  
  13.         // HttpServletRequest已在該類的字段中準備好,可直接使用  
  14.         // ......  
  15.         return SUCCESS;  
  16.     }  
  17. }  

 

 

Java代碼  複製代碼  收藏代碼
  1. // 本類將演示在Action中使用ServletRequestAware得到request的Map(注意:要使用ServletConfigInterceptor攔截器)  
  2. public class MyAction extends ActionSupport implements RequestAware {   
  3.   
  4.     Map<String, Object> request;   
  5.   
  6.     // 該方法是接口RequestAware的方法   
  7.     public void setRequest(Map<String, Object> request) {   
  8.         this.request = request;   
  9.     }   
  10.   
  11.     @Override  
  12.     public String execute() throws Exception {   
  13.         // request的Map已在該類的字段中準備好,可直接使用   
  14.         // ......   
  15.         return SUCCESS;   
  16.     }   
  17. }  
[java]  view plain copy
  1. // 本類將演示在Action中使用ServletRequestAware得到request的Map(注意:要使用ServletConfigInterceptor攔截器)  
  2. public class MyAction extends ActionSupport implements RequestAware {  
  3.   
  4.     Map<String, Object> request;  
  5.   
  6.     // 該方法是接口RequestAware的方法  
  7.     public void setRequest(Map<String, Object> request) {  
  8.         this.request = request;  
  9.     }  
  10.   
  11.     @Override  
  12.     public String execute() throws Exception {  
  13.         // request的Map已在該類的字段中準備好,可直接使用  
  14.         // ......  
  15.         return SUCCESS;  
  16.     }  
  17. }  

 

 

Java代碼  複製代碼  收藏代碼
  1. <!DOCTYPE html>   
  2. <html>   
  3.     <head>   
  4.         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">   
  5.         <title>JSP Page</title>   
  6.     </head>   
  7.     <body>   
  8.         <!-- 本JSP將演示在JSP中對request的Map的使用 -->   
  9.         <!-- 本JSP爲MyAction對應的JSP -->   
  10.   
  11.         <!-- request的Map是Struts2自動在ActionContext中存入的值(key爲request),因此使用「#」來訪問ActionContext,從中讀取request  -->   
  12.         <s:property value="#request"/>   
  13.         <!-- 如下兩行均是訪問request的Map中key爲「name」的值  -->   
  14.         <s:property value="#request.name"/>   
  15.         <s:property value="#request['name']"/>   
  16.     </body>   
  17. </html>  
[java]  view plain copy
  1. <!DOCTYPE html>  
  2. <html>  
  3.     <head>  
  4.         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
  5.         <title>JSP Page</title>  
  6.     </head>  
  7.     <body>  
  8.         <!-- 本JSP將演示在JSP中對request的Map的使用 -->  
  9.         <!-- 本JSP爲MyAction對應的JSP -->  
  10.   
  11.         <!-- request的Map是Struts2自動在ActionContext中存入的值(key爲request),因此使用「#」來訪問ActionContext,從中讀取request  -->  
  12.         <s:property value="#request"/>  
  13.         <!-- 如下兩行均是訪問request的Map中key爲「name」的值  -->  
  14.         <s:property value="#request.name"/>  
  15.         <s:property value="#request['name']"/>  
  16.     </body>  
  17. </html>  

 

 

3. Parameters,即GET請求或POST請求的參數

    Parameters爲GET或POST等請求時瀏覽器向服務器傳遞而來的參數。在傳統的Java Web項目中,使用HttpServletRequest.getParameter()等方法來獲取參數,而且能夠直接使用HttpServletRequest.getParameterMap()來得到一個封裝了參數的Map。而在Struts2中,Struts2直接把上述Map存放到了ActionContext中,key爲「parameters」。另外,ActionContext還直接提供了ActionContext.getParameters()方法來得到這個Map。所以,在Struts2的各個部件中操做parameters的方法和操做request的Map的方法十分類似,本段再也不詳述。

 

4. HttpServletSession類和session的Map

    傳統Java Web項目中的session是咱們都熟悉的,咱們用它來記錄一個用戶的會話狀態。Struts2把HttpServletSession封裝到了一個Map中,即「session的Map」,這相似對request的處理。然而爲了節省系統資源,咱們在不須要session的時候不會建立session。可能正是由於這個緣故,Struts2中沒有把HttpServletSession放入ActionContext中,若是你的程序須要使用HttpServletSession,應該先得到HttpServletRequest,而後使用getSession()或getSession(boolean b)來得到它,同時決定是否須要建立session。對於session的Map,Struts2仍然把它放入了ActionContext中(key爲"session"),可是不要擔憂,這個Map的機制使得只有put新值時纔會建立session。總之,Struts2中對HttpServletSession的操做要先經過HttpServletRequest來得到它,而對session的Map的操做與對request的Map的操做一模一樣,本段再也不詳述。

 

5. ServletContext和application的Map

    傳統的Java Web項目中,ServletContext用來存放全局變量,每一個Java虛擬機每一個Web項目只有一個ServletContext。這個ServletContext是由Web服務器建立的,來保證它的惟一性。ServletContext有一些方法能操做它的attributes,這些操做方法和操做一個Map相似。因而,Struts2又來封裝了:它把ServletContext的attributes封裝到了一個Map中,即「application的Map」,而且也放入的ActionContext中(key爲application),所以,對application的Map的操做就若是對request的Map操做,本段再也不詳述。

    至於對ServletContext的操做,與HttpServletRequest的操做相似:Struts2將ServletContext放到了 ActionContext中,而且ServletConfigInterceptor提供了對ServletContext的注入接口ServletContextAware。所以,本段再也不詳述。

    注意:在Ognl表達式中使用「#application」能夠獲得application的Map,而不是ServletContext。然而在JSP嵌入的Java代碼中(好比「<% application.getAttribute(""); %>」),application爲ServletContext,而不是Map。

 

    用一張表格來總結:

 

 

變量 從ActionContext中得到 生命週期 用Ongl來讀取值 使用ServletConfigInterceptor來注入
ActionContext類 靜態方法ActionContext. getContext() 一次Http請求 使用「#」加上key,如「#name」 沒法注入
ValueStack類 ActionContext. getValueStack() 一次Http請求 直接填寫來訪問棧中對象的方法,或者使用top來直接得到棧中對象 沒法注入
HttpServletRequest類 ActionContext. get( StrutsStatics. HTTP_REQUEST) 一次Http請求 無方便的方法 實現ServletRequestAware接口
request的Map ActionContext. get("request") 一次Http請求 使用「#request」再加上key,如「#request.name」或者「#request['name']」 實現RequestAware接口
parameters的Map ActionContext. get( "parameters") 一次Http請求 使用「# parameters」再加上key,如「#parameters .name」或者「#parameters ['name']」 實現ParameterAware接口
HttpServletSession類 無(需經過HttpServletRequest來得到) 一次Http Session會話 無方便的方法 沒法注入
session的Map ActionContext. get("session") 每次請求建立,但在一次Http Session會話中數據都是同樣的 使用「#session」再加上key,如「# session.name」或者「#session ['name']」 實現SessionAware接口
ServletContext類 ActionContext. get( StrutsStatics. SERVLET_CONTEXT) 網站項目啓動後一直存在且惟一 無方便的方法 使用ServletContextAware接口
application的Map ActionContext.get( "application") 每次請求時建立,但其中的數據是網站項目啓動後一直存在且共享 使用「# application」再加上key,如「#application .name」或者「#application ['name']」 使用ApplicationAware接口

 

附錄1 ActionContext中到底有哪些數據

 

key key的聲明處 value的類型 value.toString()

com. opensymphony. xwork2. dispatcher.

HttpServletRequest

 StrutsStatics. HTTP_REQUEST org. apache. struts2. dispatcher. StrutsRequestWrapper org. apache. struts2. dispatcher. StrutsRequestWrapper @10984e0
application  無 org. apache. struts2. dispatcher. ApplicationMap
com. opensymphony. xwork2. ActionContext. locale  ActionContext. LOCALE java. util. Locale zh_CN
com. opensymphony. xwork2. dispatcher. HttpServletResponse  StrutsStatics. HTTP_RESPONSE org. apache. catalina. connector. ResponseFacade org. apache. catalina. connector. ResponseFacade @14ecfe8

xwork. NullHandler.

createNullObjects

  Boolean false
com. opensymphony. xwork2. ActionContext. name  ActionContext. ACTION_NAME String index

com.opensymphony. xwork2.ActionContext.

conversionErrors

 ActionContext. 

CONVERSION_ERRORS

java. util. HashMap {}
com. opensymphony. xwork2. ActionContext. application  ActionContext. APPLICATION org. apache. struts2. dispatcher. ApplicationMap
attr  無 org. apache. struts2. util. AttributeMap org. apache. struts2. util. AttributeMap @133a2a8
com. opensymphony. xwork2. ActionContext. container  ActionContext. CONTAINER com. opensymphony. xwork2. inject. ContainerImpl com. opensymphony. xwork2. inject. ContainerImpl @fc02c8
com. opensymphony. xwork2. dispatcher. ServletContext  StrutsStatics. SERVLET_CONTEXT org. apache. catalina. core. ApplicationContextFacade org. apache. catalina. core. ApplicationContextFacade @11ad78c
com. opensymphony. xwork2. ActionContext. session  ActionContext. SESSION org.apache.struts2. dispatcher.SessionMap {}

com.opensymphony. xwork2.ActionContext.

actionInvocation

 ActionContext. ACTION_INVOCATION com. opensymphony. xwork2. DefaultActionInvocation com. opensymphony. xwork2. DefaultActionInvocation @13d4497
xwork. MethodAccessor. denyMethodExecution  筆者很懶,沒有找 Boolean false
report. conversion. errors  筆者很懶,沒有找 Boolean false
session  無 org. apache. struts2. dispatcher. SessionMap {}
com. opensymphony. xwork2. util. ValueStack. ValueStack  ValueStack.VALUE_STACK com. opensymphony. xwork2. ognl. OgnlValueStack com. opensymphony. xwork2. ognl. OgnlValueStack @16237fd
request  無 org. apache. struts2. dispatcher. RequestMap
action  筆者很懶,沒有找 com. example. MyAction
struts. actionMapping  筆者很懶,沒有找 org. apache. struts2. dispatcher. mapper. ActionMapping org. apache. struts2. dispatcher. mapper. ActionMapping @892cc5
parameters  無 java. util. HashMap {}
com. opensymphony. xwork2. ActionContext. parameters  ActionContext.PARAMETERS java. util. TreeMap {}

 

注意:該表格爲了排版在某些地方加了空格。

 

能夠看出,有些相同的對象被以不一樣的key屢次設置到ActionContext中。若是想看看建立ActionContext的源代碼,請看org.apache.struts2.dispatcher.Dispatcher的serviceAction方法和兩個createContextMap方法。

附錄2 Struts2標籤中value屬性直接對ActionContext訪問的問題

    經試驗並查看相關源代碼後發現,在使用<s:property value="..."/>時,該標籤的執行類會先根據value中表達式到值棧中執行表達式來查找值。若是在值棧中找到值,就返回該值;若是沒有找到,則把該表達式做爲ActionContext的key,到ActionContext中去找值。好比<s:property value="request"/>也會獲得ActionContext中的request,等價於<s:property value="#request"/>。可是,因爲標籤的執行類會認爲該值時String類型的,而且會直接進行類型轉換。因而,若是直接使用<s:property value="request"/>的話其實會讓頁面拋出異常:Request類型不能轉換成String類型。因此,只能用若是不帶#的話只能成功讀取ActionContext中String類型的值。這種機制使得某些時候棧頂的屬性能夠覆蓋ActionContext中的key,或許你正須要它。然而,鑑於這種機制的不肯定性,筆者建議訪問ActionContext中的數據必定要帶上「#」,能夠免去一些麻煩。

    關於這種轉型異常,筆者認爲是Struts2的bug,源代碼以下,當「value = getValue(expr, asType);」時,是考慮了asType的,但從context中讀取時「value = findInContext(expr);」,就沒有考慮asType,而且沒有在其餘地方看到類型檢查操做:

 

 

Java代碼  複製代碼  收藏代碼
  1. // 本代碼截取Struts2.3.1.2版本com.opensymphony.xwork2.ognl.OgnlValueStack類的第340行-352行  
  2.     private Object tryFindValue(String expr, Class asType) throws OgnlException {   
  3.         Object value = null;   
  4.         try {   
  5.             expr = lookupForOverrides(expr);   
  6.             value = getValue(expr, asType);   
  7.             if (value == null) {   
  8.                 value = findInContext(expr);   
  9.             }   
  10.         } finally {   
  11.             context.remove(THROW_EXCEPTION_ON_FAILURE);   
  12.         }   
  13.         return value;   
  14.     }  
[java]  view plain copy
  1. // 本代碼截取Struts2.3.1.2版本com.opensymphony.xwork2.ognl.OgnlValueStack類的第340行-352行  
  2.     private Object tryFindValue(String expr, Class asType) throws OgnlException {  
  3.         Object value = null;  
  4.         try {  
  5.             expr = lookupForOverrides(expr);  
  6.             value = getValue(expr, asType);  
  7.             if (value == null) {  
  8.                 value = findInContext(expr);  
  9.             }  
  10.         } finally {  
  11.             context.remove(THROW_EXCEPTION_ON_FAILURE);  
  12.         }  
  13.         return value;  
  14.     }  
相關文章
相關標籤/搜索