ActionContext作爲xwork的數據流實現的元素,作爲一個數據載體,負責數據存儲,又負責數據共享。
ValueStack是一個具備表達式引擎計算功能的數據結構。xwork將ValueStack置於ActionContext中目的爲靜態的數據添加計算功能。
xwork的執行棧:
Interceptor與Action之間形成了包裹的結構。把Action包在最裏面,棧-----interceptor-stack,在整個站的結構中,除了位於棧底的Action外,其他元素都是Interceptor對象,當試圖吧Action對象拿出來時,要先把其之上的所有Interceptor依次拿出來執行,位於棧中的interceptor,除了需要她完成自身的邏輯外,還需要指定下一步的執行對象。從源碼中,ActionContext真正的數據存儲空間,位於內部的一個Map類型的變量context,ActionContext將所有的數據對象以特定的鍵值存儲於context中,ActionContext頁提供了存取這些對象的方式。
數據共享:線程安全問題。
線程安全的判斷標準:進行數據共享的信息是類的內部實例變量,外部對內部數據的訪問是否存在多線程環境。ActionContext都滿足。。哈哈。
怎麼辦?源碼裏ActionContext內封裝了靜態的ThreadLocale的實例,而這靜態的ThreadLocale本身所操作和存儲的對象,又是ActionContext本身。所以保證ActionContext的實例都是線程安全的。使用ThreadLocale處理多線程問題,
ThreadLocal類在維護變量時,實際使用了當前線程(Thread)中的一個叫做ThreadLocalMap的獨立副本,每個線程可以獨立修改屬於自己的副本而不會互相影響,從而隔離了線程和線程,避免了線程訪問實例變量發生衝突的問題。
ThreadLocal本身並不是一個線程,而是通過操作當前線程(Thread)中的一個內部變量來達到與其他線程隔離的目的。之所以取名爲ThreadLocal,所期望表達的含義是其操作的對象是線程(Thread)的一個本地變量。如果我們看一下Thread的源碼實現,就會發現這一變量,如代碼清單4-2所示:
public class Thread implements Runnable {
// 這裏省略了許多其他的代碼
ThreadLocal.ThreadLocalMap threadLocals = null;
}
這是JDK中Thread源碼的一部分,從中我們可以看出ThreadLocalMap跟隨着當前的線程而存在。不同的線程Thread,擁有不同的ThreadLocalMap的本地實例變量,這也就是“副本”的含義。接下來我們再來看看ThreadLocal.ThreadLocalMap是如何定義的,以及ThreadLocal如何來操作它,如代碼清單4-3所示:
public class ThreadLocal<T> {
// 這裏省略了許多其他代碼
// 將value的值保存於當前線程的本地變量中
public void set(T value) {
// 獲取當前線程
Thread t = Thread.currentThread();
// 調用getMap方法獲得當前線程中的本地變量ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 如果ThreadLocalMap已存在,直接使用
if (map != null)
// 以當前的ThreadLocal的實例作爲key,存儲於當前線程的
// ThreadLocalMap中,如果當前線程中被定義了多個不同的ThreadLocal
// 的實例,則它們會作爲不同key進行存儲而不會互相干擾
map.set(this, value);
else
// ThreadLocalMap不存在,則爲當前線程創建一個新的
createMap(t, value);
}
// 獲取當前線程中以當前ThreadLocal實例爲key的變量值
public T get() {
// 獲取當前線程
Thread t = Thread.currentThread();
// 獲取當前線程中的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
// 獲取當前線程中以當前ThreadLocal實例爲key的變量值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
// 當map不存在時,設置初始值
return setInitialValue();
}
// 從當前線程中獲取與之對應的ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
// 創建當前線程中的ThreadLocalMap
void createMap(Thread t, T firstValue) {
// 調用構造函數生成當前線程中的ThreadLocalMap
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
// ThreadLoaclMap的定義
static class ThreadLocalMap {
// 這裏省略了許多代碼
}
}
從上述代碼中,我們看到了ThreadLocal類的大致結構和進行ThreadLocalMap的操作。我們可以從中得出以下的結論:
1. ThreadLocalMap變量屬於線程(Thread)的內部屬性,不同的線程(Thread)擁有完全不同的ThreadLocalMap變量。
2. 線程(Thread)中的ThreadLocalMap變量的值是在ThreadLocal對象進行set或者get操作時創建的。
3. 在創建ThreadLocalMap之前,會首先檢查當前線程(Thread)中的ThreadLocalMap變量是否已經存在,如果不存在則創建一個;如果已經存在,則使用當前線程(Thread)已創建的ThreadLocalMap。
4. 使用當前線程(Thread)的ThreadLocalMap的關鍵在於使用當前的ThreadLocal的實例作爲key進行存儲。
ThreadLocal模式,至少從兩個方面完成了數據訪問隔離,有了橫向和縱向的兩種不同的隔離方式,ThreadLocal模式就能真正地做到線程安全:
縱向隔離 —— 線程(Thread)與線程(Thread)之間的數據訪問隔離。這一點由線程(Thread)的數據結構保證。因爲每個線程(Thread)在進行對象訪問時,訪問的都是各自線程自己的ThreadLocalMap。
橫向隔離 —— 同一個線程中,不同的ThreadLocal實例操作的對象之間的相互隔離。這一點由ThreadLocalMap在存儲時,採用當前ThreadLocal的實例作爲key來保證。
ThreadLocal模式並不是什麼高深的學問,它甚至從JDK1.2開始就存在於Java世界中。由此可見,我們掌握一種知識的最終目的是熟練而合理地運用它。
ThreadLocal模式解決的是同一線程中隸屬於不同開發層次的數據共享問題,而不是在不同的開發層次中進行數據傳遞。
Java代碼
// 代碼清單1 SimpleThreadLocal
class SimpleThreadLocal {
private MapvalueMap = Collections.synchronizedMap(new HashMap());
public voidset(Object newValue) {
valueMap.put(Thread.currentThread(), newValue);//①鍵爲線程對象,值爲本線程的變量副本
}
publicObject get() {
Thread currentThread = Thread.currentThread();
Object o = valueMap.get(currentThread);// ②返回本線程對應的變量
if (o == null &&!valueMap.containsKey(currentThread)) {// ③如果在Map中不存在,放到Map
// 中保存起來。
o = initialValue();
valueMap.put(currentThread, o);
}
return o;
}
public voidremove() {
valueMap.remove(Thread.currentThread());
}
publicObject initialValue() {
return null;
}
}
兩種不同的接口訪問類型:
對xwork框架對象的訪問-----getContainer getValueStack getActionInvocation
對數據對象的訪問-----getSession、getApplication、getParameter
ActionContext所提供的數據對象的訪問接口,返回的是Map類型的數據對象而與web容器無關。
ActionContex是xwork框架所定義的元素,而xwork框架本身是脫離web容器而單獨存在的事件處理框架,因此ActionContext一旦引入web對象,勢必與xwork解耦合的基本設計思想背離。
web原生對象 | xwork封裝後的對象 |
HttpServletRequest | RequestMap |
HttpSession | SessionMap |
ServletContext | AppliactionMap |
可以通過調用ServletActionContext所暴露的接口完成對web原生容器的HttpServletRequest和HttpServletResponse的訪問。