struts2 攔截器和actioninvocation
一、Interceptor說明
Interceptor的介面定義沒有什麼特別的地方,除了init和destory方法以外,intercept方法是實現整個攔截器機制的核心方法。而它所依賴的引數ActionInvocation則是我們之前章節中曾經提到過的著名的Action排程者。
在這裡需要指出的是一個很重要的方法invocation.invoke()。這是ActionInvocation中的方法,而ActionInvocation是Action排程者,所以這個方法具備以下2層含義(詳細看DefaultActionInvocation原始碼):
1、 如果攔截器堆疊中還有其他的Interceptor,那麼invocation.invoke()將呼叫堆疊中下一個Interceptor的執行。
2、 如果攔截器堆疊中只有Action了,那麼invocation.invoke()將呼叫Action執行。
流程結構圖:
DefaultActionInvocation部分原始碼:
if (interceptors.hasNext()) { final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next(); UtilTimerStack.profile("interceptor: "+interceptor.getName(), new UtilTimerStack.ProfilingBlock<String>() {public String doProfiling() throws Exception { resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);//遞迴呼叫攔截器 return null; } }); } else { resultCode = invokeActionOnly(); }
每個攔截器中的程式碼的執行順序,在Action之前,攔截器的執行順序與堆疊中定義的一致;而在Action和Result之後,攔截器的執行順序與堆疊中定義的順序相反。
二、Interceptor攔截型別
從上面的分析,我們知道,整個攔截器的核心部分是invocation.invoke()這個函式的呼叫位置。事實上,我們也正式根據這句程式碼的呼叫位置,來進行攔截型別的區分的。在Struts2中,Interceptor的攔截型別,分成以下三類:
1、 before
before攔截,是指在攔截器中定義的程式碼,它們存在於invocation.invoke()程式碼執行之前。這些程式碼,將依照攔截器定義的順序,順序執行。
2、 after
after攔截,是指在攔截器中定義的程式碼,它們存在於invocation.invoke()程式碼執行之後。這些程式碼,將一招攔截器定義的順序,逆序執行。
3、PreResultListener
有的時候,before攔截和after攔截對我們來說是不夠的,因為我們需要在Action執行完之後,但是還沒有回到檢視層之前,做一些事情。Struts2同樣支援這樣的攔截,這種攔截方式,是通過在攔截器中註冊一個PreResultListener的介面來實現的。如:在攔截器中使用如下程式碼,其中MyPreResultListener實現了PreResultListener介面並在beforeResult方法中做了一些事情然後在攔截器類中加入
action.addPreResultListener(new MyPreResultListener());
從原始碼中,我們可以看到,我們之前提到的Struts2的Action層的4個不同的層次,在這個方法中都有體現,他們分別是:攔截器(Interceptor)、Action、PreResultListener和Result。在這個方法中,保證了這些層次的有序呼叫和執行
三、問題
使用Struts2作為web框架,知道它的攔截器(Interceptor)機制,類似與Filter和Spring的AOP,於是實現了一個為Action增加自定義前置(before)動作和後置動作(after)的攔截器(曰:WInterceptor),不過用一段時間發現,在WInterceptor的after中,對Action物件的屬性修改在頁面看不到,對請求物件的屬性設定也無效。為什麼在呼叫了Action之後(invokeAction())之後,request就不能使用了呢,攔截器不能改變Action的Result麼?
問題的關鍵在於,在呼叫actionInvocation.invoke()的之後,不僅執行類Action,也執行類Result。因而,等退回到攔截器的呼叫程式碼時,Result已經生成,View已經確定,這時你再修改模型(Action的屬性)或請求物件的屬性,對檢視不會有任何影響。
解決辦法:
方法一:使用現成的PreResultListener監聽器事件
搞清楚原因,捲起袖子幹吧,只要讓WInterpretor的after事件,放在Result的生成之前就行了。看看XWork的攔截器介面注入的actionInvocation,其實就提供增加Result執行的前置監聽事件-PreResultListener:
void addPreResultListener(PreResultListener listener);
因此,讓攔截器實現這個介面,就可以自然實現Action執行after事件了。
方法二:實現自己的ActionInvocation ,手動分離Action和Result的執行
本來前面的方法已經很好了,可是在addPreResultListener裡的異常,不會被Struts的框架捕獲,而且addPreResultListener介面不能傳遞自己的上下文引數,難道動用ThreadLocal傳參?研究了一下XWork的ActionInvocation 介面預設實現類DefaultActionInvocation,可以 寫了一個包裝類,將Action的執行和Result的生成完全分開。exeucteAction是執行Action,executeResult是執行Result。
轉載於:https://blog.csdn.net/xuchuangqi/article/details/53248306