讓咱們再來回顧一下以前咱們曾經用過的一張Action LifeCycle(生命週期)的圖: java
圖中,咱們能夠發現,Struts2的Interceptor一層一層,把Action包裹在最裏面。這樣的結構,大概有如下一些特色:
1. 整個結構就如同一個堆棧,除了Action之外,堆棧中的其餘元素是Interceptor
2. Action位於堆棧的底部。因爲堆棧"先進後出"的特性,若是咱們試圖把Action拿出來執行,咱們必須首先把位於Action上端的Interceptor拿出來執行。這樣,整個執行就造成了一個遞歸調用
3. 每一個位於堆棧中的Interceptor,除了須要完成它自身的邏輯,還須要完成一個特殊的執行職責。這個執行職責有3種選擇:
程序員
Struts2的攔截器結構的設計,其實是一個典型的責任鏈模式的應用。首先將整個執行劃分紅若干相同類型的元素,每一個元素具有不一樣的邏輯責任,並將他們歸入到一個鏈式的數據結構中(咱們能夠把堆棧結構也看做是一個遞歸的鏈式結構),而每一個元素又有責任負責鏈式結構中下一個元素的執行調用。
這樣的設計,從代碼重構的角度來看,其實是將一個複雜的系統,分而治之,從而使得每一個部分的邏輯可以高度重用並具有高度可擴展性。因此,Interceptor結構實在是Struts2/Xwork設計中的精華之筆。數據結構
Interceptor的定義
咱們來看一下Interceptor的接口的定義:app
public interface Interceptor extends Serializable { /** * Called to let an interceptor clean up any resources it has allocated. */ void destroy(); /** * Called after an interceptor is created, but before any requests are processed using * {@link #intercept(com.opensymphony.xwork2.ActionInvocation) intercept} , giving * the Interceptor a chance to initialize any needed resources. */ void init(); /** * Allows the Interceptor to do some processing on the request before and/or after the rest of the processing of the * request by the {@link ActionInvocation} or to short-circuit the processing and just return a String return code. * * @return the return code, either returned from {@link ActionInvocation#invoke()}, or from the interceptor itself. * @throws Exception any system-level error, as defined in {@link com.opensymphony.xwork2.Action#execute()}. */ String intercept(ActionInvocation invocation) throws Exception; }
Interceptor的接口定義沒有什麼特別的地方,除了init和destory方法之外,intercept方法是實現整個攔截器機制的核心方法。而它所依賴的參數ActionInvocation則是咱們以前章節中曾經提到過的著名的Action調度者。
咱們再來看看一個典型的Interceptor的抽象實現類: 框架
public abstract class AroundInterceptor extends AbstractInterceptor { /* (non-Javadoc) * @see com.opensymphony.xwork2.interceptor.AbstractInterceptor#intercept(com.opensymphony.xwork2.ActionInvocation) */ @Override public String intercept(ActionInvocation invocation) throws Exception { String result = null; before(invocation); // 調用下一個攔截器,若是攔截器不存在,則執行Action result = invocation.invoke(); after(invocation, result); return result; } public abstract void before(ActionInvocation invocation) throws Exception; public abstract void after(ActionInvocation invocation, String resultCode) throws Exception; }
在這個實現類中,實際上已經實現了最簡單的攔截器的雛形。或許你們對這樣的代碼還比較陌生,這沒有關係。我在這裏須要指出的是一個很重要的方法invocation.invoke()。這是ActionInvocation中的方法,而ActionInvocation是Action調度者,因此這個方法具有如下2層含義:
1. 若是攔截器堆棧中還有其餘的Interceptor,那麼invocation.invoke()將調用堆棧中下一個Interceptor的執行。
2. 若是攔截器堆棧中只有Action了,那麼invocation.invoke()將調用Action執行。
因此,咱們能夠發現,invocation.invoke()這個方法實際上是整個攔截器框架的實現核心。基於這樣的實現機制,咱們還能夠獲得下面2個很是重要的推論:
1. 若是在攔截器中,咱們不使用invocation.invoke()來完成堆棧中下一個元素的調用,而是直接返回一個字符串做爲執行結果,那麼整個執行將被停止。
2. 咱們能夠以invocation.invoke()爲界,將攔截器中的代碼分紅2個部分,在invocation.invoke()以前的代碼,將會在Action以前被依次執行,而在invocation.invoke()以後的代碼,將會在Action以後被逆序執行。
由此,咱們就能夠經過invocation.invoke()做爲Action代碼真正的攔截點,從而實現AOP。
Interceptor攔截類型
從上面的分析,咱們知道,整個攔截器的核心部分是invocation.invoke()這個函數的調用位置。事實上,咱們也正式根據這句代碼的調用位置,來進行攔截類型的區分的。在Struts2中,Interceptor的攔截類型,分紅如下三類:
1. before
before攔截,是指在攔截器中定義的代碼,它們存在於invocation.invoke()代碼執行以前。這些代碼,將依照攔截器定義的順序,順序執行。
2. after
after攔截,是指在攔截器中定義的代碼,它們存在於invocation.invoke()代碼執行以後。這些代碼,將一招攔截器定義的順序,逆序執行。
3. PreResultListener
有的時候,before攔截和after攔截對咱們來講是不夠的,由於咱們須要在Action執行完以後,可是尚未回到視圖層以前,作一些事情。Struts2一樣支持這樣的攔截,這種攔截方式,是經過在攔截器中註冊一個PreResultListener的接口來實現的。 ide
public interface PreResultListener { /** * This callback method will be called after the Action execution and before the Result execution. * * @param invocation * @param resultCode */ void beforeResult(ActionInvocation invocation, String resultCode); }
在這裏,咱們看到,Struts2可以支持如此多的攔截類型,與其自己的數據結構和總體設計有很大的關係。正如我在以前的文章中所提到的: 函數
由於Action是一個普通的Java類,而不是一個Servlet類,徹底脫離於Web容器,因此咱們就可以更加方便地對Control層進行合理的層次設計,從而抽象出許多公共的邏輯,並將這些邏輯脫離出Action對象自己。ui
咱們能夠看到,Struts2對於整個執行的劃分,從Interceptor到Action一直到Result,每一層都職責明確。不只如此,Struts2還爲每個層次以前都設立了恰如其分的插入點。使得整個Action層的擴展性獲得了前所未有的提高。
Interceptor執行順序
Interceptor的執行順序或許是咱們在整個過程當中最最關心的部分。根據上面所提到的概念,咱們實際上已經可以大體明白了Interceptor的執行機理。咱們來看看Struts2的Reference對Interceptor執行順序的一個形象的例子。
若是咱們有一個interceptor-stack的定義以下: this
<interceptor-stack name="xaStack"> <interceptor-ref name="thisWillRunFirstInterceptor"/> <interceptor-ref name="thisWillRunNextInterceptor"/> <interceptor-ref name="followedByThisInterceptor"/> <interceptor-ref name="thisWillRunLastInterceptor"/> </interceptor-stack>
整個攔截器棧的執行順序爲spa
在這裏,我稍微改了一下Struts2的Reference中的執行順序示例,使得整個執行順序更加可以被理解。咱們能夠看到,遞歸調用保證了各類各樣的攔截類型的執行可以層次分明。
請注意在這裏,每一個攔截器中的代碼的執行順序,在Action以前,攔截器的執行順序與堆棧中定義的一致;而在Action和Result以後,攔截器的執行順序與堆棧中定義的順序相反。
接下來咱們就來看看源碼,看看Struts2是如何保證攔截器、Action與Result三者之間的執行順序的。
以前我曾經提到,ActionInvocation是Struts2中的調度器,因此事實上,這些代碼的調度執行,是在ActionInvocation的實現類中完成的,這裏,我抽取了DefaultActionInvocation中的invoke()方法,它將向咱們展現一切。
/** * @throws ConfigurationException If no result can be found with the returned code */ public String invoke() throws Exception { String profileKey = "invoke: "; try { UtilTimerStack.push(profileKey); if (executed) { throw new IllegalStateException("Action has already executed"); } // 依次調用攔截器堆棧中的攔截器代碼執行 if (interceptors.hasNext()) { final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next(); UtilTimerStack.profile("interceptor: "+interceptor.getName(), new UtilTimerStack.ProfilingBlock<String>() { public String doProfiling() throws Exception { // 將ActionInvocation做爲參數,調用interceptor中的intercept方法執行 resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this); return null; } }); } else { resultCode = invokeActionOnly(); } // this is needed because the result will be executed, then control will return to the Interceptor, which will // return above and flow through again if (!executed) { // 執行PreResultListener if (preResultListeners != null) { for (Iterator iterator = preResultListeners.iterator(); iterator.hasNext();) { PreResultListener listener = (PreResultListener) iterator.next(); String _profileKey="preResultListener: "; try { UtilTimerStack.push(_profileKey); listener.beforeResult(this, resultCode); } finally { UtilTimerStack.pop(_profileKey); } } } // now execute the result, if we're supposed to // action與interceptor執行完畢,執行Result if (proxy.getExecuteResult()) { executeResult(); } executed = true; } return resultCode; } finally { UtilTimerStack.pop(profileKey); } }
從源碼中,咱們能夠看到,咱們以前提到的Struts2的Action層的4個不一樣的層次,在這個方法中都有體現,他們分別是:攔截器(Interceptor)、Action、PreResultListener和Result。在這個方法中,保證了這些層次的有序調用和執行。由此咱們也能夠看出Struts2在Action層次設計上的衆多考慮,每一個層次都具有了高度的擴展性和插入點,使得程序員能夠在任何喜歡的層次加入本身的實現機制改變Action的行爲。
在這裏,須要特別強調的,是其中攔截器部分的執行調用:
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
表面上,它只是執行了攔截器中的intercept方法,若是咱們結合攔截器來看,就能看出點端倪來:
public String intercept(ActionInvocation invocation) throws Exception { String result = null; before(invocation); // 調用invocation的invoke()方法,在這裏造成了遞歸調用 result = invocation.invoke(); after(invocation, result); return result; }
原來在intercept()方法又對ActionInvocation的invoke()方法進行遞歸調用,ActionInvocation循環嵌套在intercept()中,一直到語句result = invocation.invoke()執行結束。這樣,Interceptor又會按照剛開始執行的逆向順序依次執行結束。
一個有序鏈表,經過遞歸調用,變成了一個堆棧執行過程,將一段有序執行的代碼變成了2段執行順序徹底相反的代碼過程,從而巧妙地實現了AOP。這也就成爲了Struts2的Action層的AOP基礎。