在Struts2中ValueStack、ActionContext、ServletContext、request、session關係分析
1. ValueStack
ValueStack在中文版的《Struts2深入淺出》一書中譯作“值棧”。其本身資料結構是一個棧,使用者可以把一些物件(又稱作bean)存入值棧中,然後使用動態的表示式來讀取bean的屬性,或者對bean進行一些其他操作。由於值棧中可能有多個bean,值棧會按bean出棧的順序依次嘗試使用動態的表示式來讀取值,直到成功讀取值為止。在Struts2中,預設的值棧實現是OgnlValueStack,即預設使用Ognl這個動態表示式語言來讀取值。
在Struts2執行一次請求的過程中,Struts2會把當前的Action物件自動放入值棧。這樣,在渲染JSP時,JSP裡的程式碼使用<s:property value="..."/>之類標籤中的Ognl表示式會直接作用於Action物件,從而方便的讀取Action的屬性。
如何得到值棧:
- 在自定義的攔截器中,使用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的屬性。
- // 此類為一個封裝資料的簡單類,在下面的例子會用到
- public class Person {
- private String name;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
- // 本類將演示攔截器中對值棧的操作
- public class MyInterceptor extends AbstractInterceptor {
- public String intercept(ActionInvocation invocation) throws Exception {
- // 獲得值棧
- ValueStack valueStack = invocation.getStack();
- // 存入值
- Person person = new Person();
- valueStack.push(person);
- // 執行表示式獲取值
- String name = (String) valueStack.findValue("name");
- // 其他程式碼
- return invocation.invoke();
- }
- }
- // 本類將演示在Action中對值棧進行操作
- public class MyAction extends ActionSupport {
- @Override
- public String execute() throws Exception {
- // 獲得值棧
- ValueStack valueStack = ActionContext.getContext().getValueStack();
- // 存入值
- Person person = new Person();// 這是之前例子中定義的類
- valueStack.push(person);
- // 執行表示式獲取值
- String name = (String) valueStack.findValue("name");
- // 其他程式碼
- // ......
- return SUCCESS;
- }
- // 以下定義的屬性供接下來的JSP例子使用
- private String message;
- private Person person;
- private List<Person> personList;
- public String getMessage() {
- return message;
- }
- public Person getPerson() {
- return person;
- }
- public List<Person> getPersonList() {
- return personList;
- }
- }
- <%@page contentType="text/html" pageEncoding="UTF-8"%>
- <%@taglib uri="/struts-tags" prefix="s" %>
- <!DOCTYPE html>
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>JSP Page</title>
- </head>
- <body>
- <!-- 本JSP將演示在JSP中對值棧的使用 -->
- <!-- 本JSP為MyAction對應的JSP -->
- <!-- 由於Action已經被存入的值棧,所以可以呼叫Action的屬性 -->
- <!-- 使用下面的標籤和表示式來顯示MyAction的message屬性 -->
- <s:property value="message"/>
- <!-- 使用下面的標籤和表示式來呼叫Action的getText(...)方法,引數為MyAction的message屬性 -->
- <s:property value="getText(message)"/>
- <!-- 預設情況下傳遞給cssClass的是字串常量。可以使用“%{}”來啟用Ognl,這樣,傳遞給cssClass的就不是字串常量"message",而是上面所說的message的值 -->
- <s:div cssClass="%{message}"/>
- <!-- 使用s:push標籤來將物件放入值棧,如下 -->
- <s:push value="person">
- <!-- 在此s:push標籤內,值棧的棧頂元素為person,棧頂第二層為action
- <!-- 在標籤內直接呼叫person的屬性(而不是Action的屬性),如下 -->
- <s:property value="name"/>
- <!-- 在標籤內也可以使用MyAction的屬性,值棧會依次先查詢Person是否有該屬性,由於沒找到,會再MyAction中再查詢,如下 -->
- <s:property value="message"/>
- <!-- 可以使用“[0]”、“[1]”等指定從值棧的哪一層開始查詢 -->
- <!-- 此時,使用“[0]”表示從Person開始查詢,當然還是找不到,值棧就接著到MyAction中查詢,如下 -->
- <s:property value="[0].message"/>
- <!-- 此時,使用“[1]”將從MyAction開始查詢,而跳過了person,如下 -->
- <s:property value="[1].message"/>
- <!-- 想要訪問棧頂元素本身使用關鍵字“top”,比如,下面的top就代表棧頂的person,如下 -->
- <s:property value="top"/>
- <!-- 或者如下 -->
- <s:property value="[0].top"/>
- <!-- 想要訪問MyAction本身的話使用如下寫法 -->
- <s:property value="[1].top"/>
- </s:push>
- <!-- 此時person已被移出值棧,再使用如下標籤和表示式將無法得到值 -->
- <!--<s:property value="name"/>-->
- <!-- iterator標籤會把list的每個元素依次存入棧頂,如下 -->
- <s:iterator value="personList">
- <!-- 獲得List每個元素中的name屬性 -->
- <s:property value="name"/>
- </s:iterator>
- </body>
- </html>
2.ActionContext
ActionContext是Action的上下文,Struts2自動在其中儲存了一些在Action執行過程中所需的物件,比如session, parameters, locale等。Struts2會根據每個執行HTTP請求的執行緒來建立對應的ActionContext,即一個執行緒有一個唯一的ActionContext。因此,使用者可以使用靜態方法ActionContext.getContext()來獲取當前執行緒的ActionContext,也正是由於這個原因,使用者不用去操心讓Action是執行緒安全的。
無論如何,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}的表示式”。(“#”的更多用途參見這裡)
- // 本類將演示攔截器中對ActionContext的操作
- public class MyInterceptor extends AbstractInterceptor {
- public String intercept(ActionInvocation invocation) throws Exception {
- // 獲得ActionContext
- ActionContext actionContext = invocation.getInvocationContext();
- // 存入值
- Person person = new Person();
- actionContext.put("person", person);
- // 獲取值
- Object value = actionContext.get("person");
- // 獲取HttpServletRequest
- HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);
- // 獲取request的Map,即HttpServletRequest.getAttribute(...)和HttpServletRequest.setAttribute(...)所操作的值
- Map requestMap = (Map) actionContext.get("request");
- // 其他程式碼
- // ......
- return invocation.invoke();
- }
- }
- // 本類將演示在Action中對ActionContext進行操作
- public class MyAction extends ActionSupport {
- @Override
- public String execute() throws Exception {
- // 獲得值棧
- ActionContext actionContext = ActionContext.getContext();
- // 存入值
- Person person = new Person();// 這是之前例子中定義的類
- actionContext.put("person", person);
- // 獲取值
- Object object = actionContext.get("person");
- // 其他程式碼
- // ......
- return SUCCESS;
- }
- }
- <!DOCTYPE html>
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>JSP Page</title>
- </head>
- <body>
- <!-- 本JSP將演示在JSP中對ActionContext的使用 -->
- <!-- 本JSP為MyAction對應的JSP -->
- <!-- 由於Action中已經向ActionContext存入了key為"person"的值,所以可以使用“#person”來獲取它,如下 -->
- <s:property value="#person"/>
- <!-- 獲得person的name屬性,如下 -->
- <s:property value="#person.name"/>
- <!-- 獲得Struts2在ActionContext中存入的值,比如request的Map,如下 -->
- <s:property value="#request"/>
- <!-- 獲得Struts2在ActionContext中存入的值,比如session的Map,如下 -->
- <s:property value="#session"/>
- <!-- 獲得Struts2在ActionContext中存入的值,request請求傳遞的GET引數或POST引數的Map,如下 -->
- <s:property value="#parameters"/>
- <!-- 以下演示在JSP中把值存入ActionContext中 -->
- <!-- 存入一個字串"myName",key為"myKey",如下 -->
- <s:set value="%{'myName'}" var="myKey"/>
- <!-- 使用s:bean標籤來建立一個物件,並把它存入ActionContext中,key為myObject,如下 -->
- <s:bean name="com.example.Person" var="myObject"/>
- <!-- 之後就可以用“#”來讀取它們,如下 -->
- <s:property value="#myKey"/>
- <s:property value="#myObject"/>
- </body>
- </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專案沒有區別,本文不再詳述。
使用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傳值。
一些例子:
- // 本類將演示攔截器中對HttpServletRequest和request的Map的操作
- public class MyInterceptor extends AbstractInterceptor {
- public String intercept(ActionInvocation invocation) throws Exception {
- // 獲得ActionContext
- ActionContext actionContext = invocation.getInvocationContext();
- // 獲得HttpServletRequest
- HttpServletRequest httpServletRequest=(HttpServletRequest)actionContext.get(StrutsStatics.HTTP_REQUEST);
- // 獲得request的Map
- Map requestMap = (Map) actionContext.get("request");
- // 建立一個類作為例項
- Person person = new Person();
- // 以下兩行的語句作用相同
- httpServletRequest.setAttribute("person", person);
- requestMap.put("person", person);
- // 其他程式碼
- // ......
- return invocation.invoke();
- }
- }
- // 本類將演示在Action中對HttpServletRequest和request的Map進行操作(靜態方法獲得ActionContext)
- public class MyAction extends ActionSupport {
- @Override
- public String execute() throws Exception {
- // 獲得ActionContext
- ActionContext actionContext = ActionContext.getContext();
- // 獲得HttpServletRequest
- HttpServletRequest httpServletRequest=(HttpServletRequest)actionContext.get(StrutsStatics.HTTP_REQUEST);
- // 獲得request的Map
- Map requestMap = (Map) actionContext.get("request");
- // 建立一個類作為例項
- Person person = new Person();
- // 以下兩行的語句作用相同
- httpServletRequest.setAttribute("person", person);
- requestMap.put("person", person);
- // 其他程式碼
- // ......
- return SUCCESS;
- }
- }
- // 本類將演示在Action中使用ServletRequestAware獲得HttpServletRequest(注意:要使用ServletConfigInterceptor攔截器)
- public class MyAction extends ActionSupport implements ServletRequestAware {
- private HttpServletRequest request;
- //此方法是介面ServletRequestAware的方法
- public void setServletRequest(HttpServletRequest request) {
- this.request = request;
- }
- @Override
- public String execute() throws Exception {
- // HttpServletRequest已在該類的欄位中準備好,可直接使用
- // ......
- return SUCCESS;
- }
- }
- // 本類將演示在Action中使用ServletRequestAware獲得request的Map(注意:要使用ServletConfigInterceptor攔截器)
- public class MyAction extends ActionSupport implements RequestAware {
- Map<String, Object> request;
- // 該方法是介面RequestAware的方法
- public void setRequest(Map<String, Object> request) {
- this.request = request;
- }
- @Override
- public String execute() throws Exception {
- // request的Map已在該類的欄位中準備好,可直接使用
- // ......
- return SUCCESS;
- }
- }
- <!DOCTYPE html>
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>JSP Page</title>
- </head>
- <body>
- <!-- 本JSP將演示在JSP中對request的Map的使用 -->
- <!-- 本JSP為MyAction對應的JSP -->
- <!-- request的Map是Struts2自動在ActionContext中存入的值(key為request),所以使用“#”來訪問ActionContext,從中讀取request -->
- <s:property value="#request"/>
- <!-- 以下兩行均是訪問request的Map中key為“name”的值 -->
- <s:property value="#request.name"/>
- <s:property value="#request['name']"/>
- </body>
- </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,並且沒有在其他地方看到型別檢查操作:
- // 本程式碼擷取Struts2.3.1.2版本com.opensymphony.xwork2.ognl.OgnlValueStack類的第340行-352行
- private Object tryFindValue(String expr, Class asType) throws OgnlException {
- Object value = null;
- try {
- expr = lookupForOverrides(expr);
- value = getValue(expr, asType);
- if (value == null) {
- value = findInContext(expr);
- }
- } finally {
- context.remove(THROW_EXCEPTION_ON_FAILURE);
- }
- return value;
- }
相關文章
- ServletContext,ActionContext,ServletActionContextServletContext
- 關於struts2中ActionContext的實現原理薦Context
- STRUTS2獲得session和requestSession
- Struts2 ActionContext(二十四)Context
- struts2中session使用Session
- Struts2第九篇【OGNL、valueStack詳解】
- Cookie與Session 關係CookieSession
- session和cookie關係SessionCookie
- http中session和cookie的區別和關係HTTPSessionCookie
- oracle session和process的關係OracleSession
- connection和session的關係Session
- SpringFlux中Request與HandlerMapping關係的形成過程SpringUXAPP
- 通過11G的V$SESSION來分析鎖阻塞關係Session
- Cookie 和 Session 關係和區別CookieSession
- Struts2中使用Session的兩種方法Session
- Session store not set on request.Session
- Laravel - Session store not set on requestLaravelSession
- 【Struts2】Session的獲取Session
- oracle session和process的關係 .轉自CSDNOracleSession
- 多版本cursor,與session_cached_cursors關係Session
- Struts2所有依賴關係(struts-2.5.14.1)
- 架構之路(9):Session Per Request架構Session
- 架構之路(九)Session Per Request架構Session
- Spring在單例bean中使用session、request範圍的beanSpring單例BeanSession
- ArrayList繼承關係分析繼承
- 理解cookie、session、localStorage、sessionStorage的關係與區別CookieSession
- 我要追蹤一條語句是誰做的,但是這些資訊在V$session中關聯關係是怎樣的?Session
- 在Java中,以下有關類的關聯關係描述錯誤的是()Java
- ORACLE EBS的Request ID和後臺資料庫Session ID關聯Oracle資料庫Session
- 終於搞懂Spring中Scope為Request和Session的Bean了SpringSessionBean
- 因果關係分析方法簡介
- 關於session leak的問題分析Session
- 關係先行 銷售在後
- Windows系統中owner owned 關係和 parent child關係Windows
- 在Struts2中寫介面,返回json格式JSON
- 80/20定律在客戶關係管理(CRM)中的應用
- beego Session main 在 ctx.Request.RequestURI放行一個get post 請求問題GoSessionAI
- 在asp.net handler 中 使用 sessionASP.NETSession