struts2攔截器的實現原理及源碼剖析

攔截器(interceptor)是Struts2最強大的特性之一,也能夠說是struts2的核心,攔截器可讓你在Actionresult被執行以前或以後進行一些處理。同時,攔截器也可讓你將通用的代碼模塊化並做爲可重用的類。Struts2中的不少特性都是由攔截器來完成的。攔截是AOP的一種實現策略。在Webwork的中文文檔的解釋爲:攔截器是動態攔截Action調用的對象。它提供了一種機制可使開發者能夠定義在一個action執行的先後執行的代碼,也能夠在一個action執行前阻止其執行。同時也是提供了一種能夠提取action中可重用的部分的方式。談到攔截器,還有一個詞你們應該知道——攔截器鏈(Interceptor Chain,在Struts 2中稱爲攔截器棧Interceptor Stack)。攔截器鏈就是將攔截器按必定的順序聯結成一條鏈。在訪問被攔截的方法或字段時,攔截器鏈中的攔截器就會按其以前定義的順序被調用。java

一.攔截器的實現原理:程序員

  大部分時候,攔截器方法都是經過代理的方式來調用的。Struts 2的攔截器實現相對簡單。當請求到達Struts 2的ServletDispatcher時,Struts 2會查找配置文件,並根據其配置實例化相對的攔截器對象,而後串成一個列表(list),最後一個一個地調用列表中的攔截器。事實上,咱們之因此可以如此靈活地使用攔截器,徹底歸功於動態代理的使用。動態代理是代理對象根據客戶的需求作出不一樣的處理。對於客戶來講,只要知道一個代理對象就好了。那Struts2中,攔截器是如何經過動態代理被調用的呢?當Action請求到來的時候,會由系統的代理生成一個Action的代理對象,由這個代理對象調用Actionexecute()或指定的方法,並在struts.xml中查找與該Action對應的攔截器。若是有對應的攔截器,就在Action的方法執行前(後)調用這些攔截器;若是沒有對應的攔截器則執行Action的方法。其中系統對於攔截器的調用,是經過ActionInvocation來實現的。代碼以下:
app

if (interceptors.hasNext()) {  
Interceptor interceptor=(Interceptor)interceptors.next();  
resultCode = interceptor.intercept(this);  
 else {  
if (proxy.getConfig().getMethodName() == null) {  
resultCode = getAction().execute();  
} else {  
resultCode = invokeAction(getAction(), proxy.getConfig());  
}  
}



能夠發現Action並無與攔截器發生直接關聯,而徹底是代理在組織Action與攔截器協同工做。以下圖:框架

                                                              


二.攔截器執行分析ide

     咱們你們都知道,Interceptor的接口定義沒有什麼特別的地方,除了initdestory方法之外,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中的方法,而ActionInvocationAction調度者,因此這個方法具有如下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代碼真正的攔截點,從而實現AOPthis


三.源碼解析spa


          下面咱們經過查看源碼來看看Struts2是如何保證攔截器、ActionResult三者之間的執行順序的。以前我曾經提到,ActionInvocationStruts2中的調度器,因此事實上,這些代碼的調度執行,是在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);

    }

}

 

      從源碼中,咱們能夠看到Action層的4個不一樣的層次,在這個方法中都有體現,他們分別是:攔截器(Interceptor)、ActionPreResultListenerResult。在這個方法中,保證了這些層次的有序調用和執行。由此咱們也能夠看出Struts2Action層次設計上的衆多考慮,每一個層次都具有了高度的擴展性和插入點,使得程序員能夠在任何喜歡的層次加入本身的實現機制改變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()方法又對ActionInvocationinvoke()方法進行遞歸調用,ActionInvocation循環嵌套在intercept()中,一直到語句result = invocation.invoke()執行結束。這樣,Interceptor又會按照剛開始執行的逆向順序依次執行結束。一個有序鏈表,經過遞歸調用,變成了一個堆棧執行過程,將一段有序執行的代碼變成了2段執行順序徹底相反的代碼過程,從而巧妙地實現了AOP這也就成爲了Struts2Action層的AOP基礎。



攔截器和過濾器之間有不少相同之處,可是二者之間存在根本的差異。其主要區別爲如下幾點:
1)攔截器是基於JAVA反射機制的,而過濾器是基於函數回調的。
2)過濾器依賴於Servlet容器,而攔截器不依賴於Servlet容器
3)攔截器只能對Action請求起做用,而過濾器能夠對幾乎全部的請求起做用。
4)攔截器能夠訪問Action上下文、值棧裏的對象,而過濾器不能
5)在Action的生命週期中,攔截器能夠屢次被調用,而過濾器只能在容器初始化時被調用一次。

相關文章
相關標籤/搜索