<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t0">valuestack</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t1">如何得到值棧:</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t2">如何将對象存入值棧:</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t3">讓值棧執行表達式來獲得值:</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t4">在jsp中跳過棧頂元素直接通路第二層:</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t5">在jsp中通路值棧對象本身(而不是它們的屬性)</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t6">actioncontext</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t7">valuestack與actioncontext的聯系和差別:</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t8">如何獲得actioncontext:</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t9">如何向actioncontext中存入值:</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t10">如何從actioncontext中讀取值:</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t11">httpservletrequest類或request的map</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t12">使用httpservletrequest類還是request的map</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t13">使用request的map還是actioncontext:</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t14">如何獲得httpservletrequest:</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t15">如何獲得request的map:</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t16">parameters,即get請求或post請求的參數</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t17">httpservletsession類和session的map</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t18">servletcontext和application的map</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t19">附錄1 actioncontext中到底有哪些資料</a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t20"></a>
<a target="_blank" href="http://blog.csdn.net/ambow_cq/article/details/7458810#t21">附錄2 struts2标簽中value屬性直接對actioncontext通路的問題</a>
筆者不知道該用哪個詞來形容valuestack、actioncontext等可以在struts2中用來存放資料的類。這些類使用的範圍不同,得到的方法也不同,下面就來一一介紹。
聲明:本文參考struts2版本為2.3.1.2,内容僅供參考,限于筆者水準有限,難免有所疏漏,望您能友善指出。本文發表于iteye,謝絕轉載。
在struts2執行一次請求的過程中,struts2會把目前的action對象自動放入值棧。這樣,在渲染jsp時,jsp裡的代碼使用<s:property value="..."/>之類标簽中的ognl表達式會直接作用于action對象,進而友善的讀取action的屬性。
在jsp中,直接使用标簽即可獲得值棧裡的資料,而一般不用擷取值棧本身。
struts2自動存入action:之前已經提到,struts2在執行一次請求的過程中會把目前的action對象自動存入值棧中。
在自定義的攔截器中存入值棧:得到值棧對象後調用valuestack.put(object object)方法。
在action類中存入值棧:得到值棧對象後調用valuestack.put(object object)方法。
在自定義的攔截器中,獲得值棧後,使用valuestack.findvalue(...)等方法。
在action類中,獲得值棧後,使用valuestack.findvlaue(...)等方法。
在表示式中使用top關鍵字來通路對象本身。比如,表達式“name”等價于“top.name”,表達式“[0].top”等價于“top”,表達式“[1].top.name”等價于“[1].name”。
總之,值棧主要目的是為了讓jsp内能友善的通路action的屬性。
一些例子:
view
code
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 }
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 }
1 // 本類将示範在action中對值棧進行操作
2 public class myaction extends actionsupport {
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 }
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>
無論如何,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也能很友善的通路它)。
相同點:它們都是在一次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)。
在自定義的攔截器中:使用actioninvocation.getinvocationcontext()或者使用actioncontext.getcontext()。
在action類中:讓攔截器注入或者使用actioncontext.getcontext()。
在非action類中:讓action類傳遞參數、使用注入機制注入或者使用actioncontext.getcontext()。注意:隻有運作在request線程中的代碼才能調用actioncontext.getcontext(),否則傳回的是null。
在jsp中:一般不需要獲得actioncontext本身。
在攔截器、action類、非action類等java類中:使用actioncontext.put(object key, object value)方法。
在攔截器、action類、非action類等java類中:使用actioncontext.get(object key)方法。
總之,在jsp中使用actioncontext一方面是由于它是映射結構,另一方面是能讀取action的一些配置。當你需要為許多action提供通用的值的話,可以讓每個action都提供getxxx()方法,但更好的方法是在攔截器或jsp模闆中把這些通用的值存放到actioncontext中(因為攔截器或jsp模闆往往通用于多個action)。
1 // 本類将示範攔截器中對actioncontext的操作
5 // 獲得actioncontext
6 actioncontext actioncontext = invocation.getinvocationcontext();
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 }
1 // 本類将示範在action中對actioncontext進行操作
7 actioncontext actioncontext = actioncontext.getcontext();
10 actioncontext.put("person", person);
11 // 擷取值
12 object object = actioncontext.get("person");
17 }
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>
struts2中提供了兩種對request的操作:一種是web伺服器提供的httpservletrequest類,這和傳統java web項目中的操作request的方式相同;另一種是一個“request的map”,即封裝了httpservletrequest的attributes的映射類,操作該map相當于操作httpservletrequest的attributes。之是以提供了map的操作方式,一是友善操作,二是能友善使用ognl在jsp标簽中讀取request。無論如何,這兩個request是互通的。至于request的生命周期等概念,與其他的java
web項目沒有差別,本文不再詳述。
雖然兩者是互通的,但就讀取request的attributes而言,使用request的map要友善許多,并且不會暴露不必要的接口。當然,httpservletrequest有一些request的map沒有的方法,使用這些方法時當然還是要用前者。
兩者都是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的資料。
如果已經有actioncontext,則使用“actioncontext.get(strutsstatics.http_request)”來獲得httpservletrequest。
在自定義的攔截器中,先獲得actioncontext,再通過actioncontext來獲得。
在jsp中,一般不需要獲得httpservletrequest。
如果已經有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傳值。
1 // 本類将示範攔截器中對httpservletrequest和request的map的操作
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);
1 // 本類将示範在action中對httpservletrequest和request的map進行操作(靜态方法獲得actioncontext)
6 // 獲得actioncontext
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 }
1 // 本類将示範在action中使用servletrequestaware獲得httpservletrequest(注意:要使用servletconfiginterceptor攔截器)
2 public class myaction extends actionsupport implements servletrequestaware {
4 private httpservletrequest request;
5
6 //此方法是接口servletrequestaware的方法
7 public void setservletrequest(httpservletrequest request) {
8 this.request = request;
9 }
11 @override
12 public string execute() throws exception {
13 // httpservletrequest已在該類的字段中準備好,可直接使用
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 -->
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>
1 // 本類将示範在action中使用servletrequestaware獲得request的map(注意:要使用servletconfiginterceptor攔截器)
2 public class myaction extends actionsupport implements requestaware {
4 map<string, object> request;
5
6 // 該方法是接口requestaware的方法
7 public void setrequest(map<string, object> request) {
13 // request的map已在該類的字段中準備好,可直接使用
parameters為get或post等請求時浏覽器向伺服器傳遞而來的參數。在傳統的java web項目中,使用httpservletrequest.getparameter()等方法來擷取參數,并且可以直接使用httpservletrequest.getparametermap()來獲得一個封裝了參數的map。而在struts2中,struts2直接把上述map存放到了actioncontext中,key為“parameters”。另外,actioncontext還直接提供了actioncontext.getparameters()方法來獲得這個map。是以,在struts2的各個部件中操作parameters的方法和操作request的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的操作如出一轍,本段不再詳述。
傳統的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來讀取值
actioncontext類
靜态方法actioncontext. getcontext()
一次http請求
使用“#”加上key,如“#name”
無法注入
valuestack類
actioncontext. getvaluestack()
直接填寫來通路棧中對象的方法,或者使用top來直接獲得棧中對象
httpservletrequest類
actioncontext. get( strutsstatics. http_request)
無友善的方法
request的map
actioncontext. get("request")
使用“#request”再加上key,如“#request.name”或者“#request['name']”
parameters的map
actioncontext. get( "parameters")
使用“# parameters”再加上key,如“#parameters .name”或者“#parameters ['name']”
httpservletsession類
無(需通過httpservletrequest來獲得)
一次http session會話
session的map
actioncontext. get("session")
每次請求建立,但在一次http session會話中資料都是一樣的
使用“#session”再加上key,如“# session.name”或者“#session ['name']”
servletcontext類
actioncontext. get( strutsstatics. servlet_context)
網站項目啟動後一直存在且唯一
application的map
actioncontext.get( "application")
每次請求時建立,但其中的資料是網站項目啟動後一直存在且共享
使用“# application”再加上key,如“#application .name”或者“#application ['name']”
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
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
actioninvocation
actioncontext. action_invocation
com. opensymphony. xwork2. defaultactioninvocation
com. opensymphony. xwork2. defaultactioninvocation @13d4497
xwork. methodaccessor. denymethodexecution
筆者很懶,沒有找
report. conversion. errors
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
com. opensymphony. xwork2. actioncontext. parameters
actioncontext.parameters
java. util. treemap
注意:該表格為了排版在某些地方加了空格。
可以看出,有些相同的對象被以不同的key多次設定到actioncontext中。如果想看看建立actioncontext的源代碼,請看org.apache.struts2.dispatcher.dispatcher的serviceaction方法和兩個createcontextmap方法。
<a target="_blank"></a>
經試驗并檢視相關源代碼後發現,在使用<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,并且沒有在其他地方看到類型檢查操作:
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 }