Interceptor的定義
我們來看一下Interceptor的接口的定義:
Java代碼
publicinterfaceInterceptorextendsSerializable?{
/**
*?Called?to?let?an?interceptor?clean?up?any?resources?it?has?allocated.
*/
voiddestroy();
/**
*?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.
*/
voidinit();
/**
*?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)throwsException;
}
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的抽象實現類:
Java代碼
publicabstractclassAroundInterceptorextendsAbstractInterceptor?{
/*?(non-Javadoc)
*?@see?com.opensymphony.xwork2.interceptor.AbstractInterceptor#intercept(com.opensymphony.xwork2.ActionInvocation)
*/
@Override
publicString?intercept(ActionInvocation?invocation)throwsException?{
String?result?=null;
before(invocation);
//?調用下一個攔截器,如果攔截器不存在,則執行Action
result?=?invocation.invoke();
after(invocation,?result);
returnresult;
}
publicabstractvoidbefore(ActionInvocation?invocation)throwsException;
publicabstractvoidafter(ActionInvocation?invocation,?String?resultCode)throwsException;
}
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的接口來實現的。
Java代碼
publicinterfacePreResultListener?{
/**
*?This?callback?method?will?be?called?after?the?Action?execution?and?before?the?Result?execution.
*
*?@param?invocation
*?@param?resultCode
*/
voidbeforeResult(ActionInvocation?invocation,?String?resultCode);
}
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能夠支持如此多的攔截類型,與其本身的數據結構和整體設計有很大的關系。正如我在之前的文章中所提到的:
downpour 寫道
因為Action是一個普通的Java類,而不是一個Servlet類,完全脫離于Web容器,所以我們就能夠更加方便地對Control層進行合理的層次設計,從而抽象出許多公共的邏輯,并將這些邏輯脫離出Action對象本身。
我們可以看到,Struts2對于整個執行的劃分,從Interceptor到Action一直到Result,每一層都職責明確。不僅如此,Struts2還為每一個層次之前都設立了恰如其分的插入點。使得整個Action層的擴展性得到了史無前例的提升。
Interceptor執行順序
Interceptor的執行順序或許是我們在整個過程中最最關心的部分。根據上面所提到的概念,我們實際上已經能夠大致明白了Interceptor的執行機理。我們來看看Struts2的Reference對Interceptor執行順序的一個形象的例子。
如果我們有一個interceptor-stack的定義如下:
Xml代碼
那么,整個執行的順序大概像這樣:
在這里,我稍微改了一下Struts2的Reference中的執行順序示例,使得整個執行順序更加能夠被理解。我們可以看到,遞歸調用保證了各種各樣的攔截類型的執行能夠井井有條。
請注意在這里,每個攔截器中的代碼的執行順序,在Action之前,攔截器的執行順序與堆棧中定義的一致;而在Action和Result之后,攔截器的執行順序與堆棧中定義的順序相反。