1. 值棧
值棧是對應(yīng)每一個請求的輕量級的內(nèi)存數(shù)據(jù)中心,其實(shí)也是上一章講的數(shù)據(jù)流元素ActionContext與ValueStack.
從廣義上講,值棧就是ActionContext,它是action的上下文環(huán)境.而從狹義上講,值棧僅僅是指ValueStack.
ValueStack是ActionContext的一個組成部分.
數(shù)據(jù)流有兩個特性:數(shù)據(jù)和流.數(shù)據(jù)強(qiáng)調(diào)的是作為一個載體,流強(qiáng)調(diào)數(shù)據(jù)的訪問和傳輸.
ActionContext作為數(shù)據(jù)的載體,既負(fù)責(zé)數(shù)據(jù)的存儲也負(fù)責(zé)數(shù)據(jù)共享.
而ValueStack是一個具備表達(dá)式引擎計(jì)算能力的數(shù)據(jù)結(jié)構(gòu),是為了解決數(shù)據(jù)訪問和數(shù)據(jù)傳輸而定義得到特殊形象.
所以,Xwork將ValueStack置于ActionContext中的目的在于為靜態(tài)的數(shù)據(jù)添加動態(tài)計(jì)算的功能.
2. ActionContext
ActionContext是action的上下文環(huán)境.ActionContext真正的數(shù)據(jù)存儲空間,是Map類型的變量context.ActionContext將所有的數(shù)據(jù)對象都以特定的鍵值存儲與context之中.同時為了方便,提供了一些取值的快捷方式,如getValueStack,getSession.
-
2.1 數(shù)據(jù)共享
在數(shù)據(jù)共享的時候,如何保證"線程安全"呢.
public class ActionContext implements Serializable {
static ThreadLocal actionContext = new ThreadLocal();
……
}
從源碼可以看出,在ActionContext內(nèi)部封裝了一個ThreadLocal實(shí)例,而ThreadLocal實(shí)例所操作和存儲的對象,又是ActionContext.這保證了其線程安全.
-
2.2 數(shù)據(jù)存儲
ActionContext中存放了很多內(nèi)容(包括action自身),大致可以分為兩類:
對XWork框架對象的訪:getContainer,getValueStack,getActionInvocation等等..
對數(shù)據(jù)對象的訪問:getApplication,getSession,getParameters,getName等等..
值得注意的是,ActionContext對數(shù)據(jù)對象的訪問,得到的都是一個Map對象而不是類似HttpSession或者ServletContext這樣純正的Web容器對象.這主要還是因?yàn)?strong>Xwork與Web容器的解耦.
解耦之后可以對兩個方面做到更好:
- 被封裝后的SessionMap等對象,進(jìn)一步保證數(shù)據(jù)訪問的線程安全性.
- 保持所有存儲對象的Map結(jié)構(gòu),都有統(tǒng)一的數(shù)據(jù)訪問方式.
當(dāng)然我們還有更多存儲數(shù)據(jù)的方式.
使用xxxAware接口:可以使用類似SessionAware,RequestAware之類的接口通過使用IoC/DI來為Action注入Map.
使用ServletActionContext:這個類直接繼承了ActionContext,并且它能直接取到Servlet的相關(guān)對象,例如getRequest取到的就是HttpServletRequest.同時,使用這個子類同樣可以通過IOC/DI的方式注入,不過好像顯得多此一舉.
3. ValueStack
-
3.1 OGNL
OGNL是對象圖導(dǎo)航語言O(shè)bject-Graph Navigation Language的縮寫,它是一種功能強(qiáng)大的表達(dá)式語言(Expression Language,簡稱為EL),通過它簡單一致的表達(dá)式語法,可以存取對象的屬性,調(diào)用對象的方法,遍歷整個對象的結(jié)構(gòu)圖,實(shí)現(xiàn)字段類型轉(zhuǎn)化等功能.它使用相同的表達(dá)式去存取對象的屬性.
所謂對象圖,即以任意一個對象為根,通過OGNL可以訪問與這個對象關(guān)聯(lián)的其它對象.
Emp emp=new Emp();
DepartMent department=new DepartMent();
Enterprise enterprise=new Enterprise();
enterprise.setName("A");
department.setEnterPrise(enterprise);
emp.setDepartment(department);
那么利用OGNL導(dǎo)航就可以是
String value=(String)Ognl.getValue("department.enterprise.name",emp);
第一個參數(shù)是OGNL表達(dá)式,而第二個參數(shù)則是root對象.
由于OGNL中不支持多個root對象,所以如果要訪問多個不相干的對象,就需要一個context上下文對象,它是一個Map類型的對象.
Emp emp=new Emp();
emp.setName("張三");
Emp emp2=new Emp();
emp2.setName("李四");
Emp emp3=new Emp();
emp3.setName("王五");
Map context=new HashMap();
context.put("e1",emp);
context.put("e2",emp2);
String value=(String)Ognl.getValue("#e1.name+','+#e2.name+','+name",context,emp3);
這里把emp1和emp2存儲到map中,取值時候需要加#,而放在root中的emp3則可以直接取值.
OGNL還可以訪問數(shù)組與集合,如果數(shù)組與集合在context中,那么類似如下取值
Ognl.getValue("#list[0]",context,root);
Ognl.getValue("#array[0]",context,root);
Ognl.getValue("#map['key']",context,root);
Ognl.getValue("#map.key",context,root);
如果是存放在root中的,那么就類似#root[0].value
以上方式可以組合使用.放到Struts2中考慮一些復(fù)雜的例子:
- 要獲取Session中一個key值為“users”的List,對應(yīng)的OGNL應(yīng)為#session[‘users’],或者#session.users
- 要操作這個List的第3個元素,對應(yīng)的OGNL應(yīng)為#session[‘users’][2],或者#session.users[2]
- 要操作這個對象的userId屬性,對應(yīng)的OGNL應(yīng)為#session[‘users’][2].userId,或者#session.users[2].userId
另外OGNL還可以進(jìn)行賦值操作,直接獲取root對象的方法,或者用@符號獲取靜態(tài)變量和方法等等,更多的知識可以去http://www.ognl.org.
-
3.2 ValueStack
valuestack是對OGNL的一個擴(kuò)展.我們知道OGNL有三要素,表達(dá)式,context對象以及root對象.而valuestack的擴(kuò)展是針對root對象的.主要是,valuestack可以將一組對象都視為root對象,而在原生ognl中,root對象只有一個.
valuestack從抽象層面上講是一個棧,后入先出的鏈表結(jié)構(gòu).而valuestack實(shí)際上是一個接口,OgnlValueStack是其實(shí)現(xiàn)類.
觀察源碼可知,OgnlValueStack起核心作用的是一個叫CompoudRoot的數(shù)據(jù)結(jié)構(gòu),而它繼承于ArrayList.
知道了ValueStack的數(shù)據(jù)結(jié)構(gòu)后,來看看其對OGNL計(jì)算規(guī)則的影響.
由于可以有多個root對象(包括action本身),在進(jìn)行表達(dá)式匹配的時候,從棧的頂端開始自上而下對每個棧內(nèi)元素進(jìn)行遍歷匹配計(jì)算 .返回第一個成功匹配的結(jié)果.
另外,有兩個重要的概念:棧頂元素和子棧.
所謂棧頂元素,就是可以通過[0]進(jìn)行訪問的元素,同時也可以通過top進(jìn)行訪問.
而子棧,就是出去棧頂元素以外的棧結(jié)構(gòu).[n]表示除去棧結(jié)構(gòu)中前n個元素之后所構(gòu)成的棧.
一個大小為N的ValueStack,除了自身,有N-1個子棧
每一個子棧自身也是一個ValueStack,構(gòu)成遞歸的數(shù)據(jù)結(jié)構(gòu)
顯然我們可以用top訪問第一個元素,用[1].top訪問第二個元素.
4. 水乳交融的ActionContext與ValueStack
水乳交融用來描述兩者之間的關(guān)系.在學(xué)習(xí)的時候,這種密切會帶來一些困擾,至少我之前是的.
ActionContext的創(chuàng)建,總是伴隨著ValueStack的創(chuàng)建
緊接著ValueStack的創(chuàng)建就是ActionContext的創(chuàng)建,而ActionContext的創(chuàng)建以ValueStack的上下文環(huán)境作為參數(shù).兩者幾乎是相同時刻創(chuàng)建出來的.
ValueStack的上下文環(huán)境與ActionContext的數(shù)據(jù)存儲空間一致
意味著
ValueStack.getContext()==ActionContext.getContext().getContextMap()
說明兩者可以互相得到.