Struts2 攔截器 Interceptor結構

Interceptor結構 

讓咱們再來回顧一下以前咱們曾經用過的一張Action LifeCycle(生命週期)的圖: java

圖中,咱們能夠發現,Struts2的Interceptor一層一層,把Action包裹在最裏面。這樣的結構,大概有如下一些特色: 

1. 整個結構就如同一個堆棧,除了Action之外,堆棧中的其餘元素是Interceptor 

2. Action位於堆棧的底部。因爲堆棧"先進後出"的特性,若是咱們試圖把Action拿出來執行,咱們必須首先把位於Action上端的Interceptor拿出來執行。這樣,整個執行就造成了一個遞歸調用 

3. 每一個位於堆棧中的Interceptor,除了須要完成它自身的邏輯,還須要完成一個特殊的執行職責。這個執行職責有3種選擇: 
 程序員

  • 停止整個執行,直接返回一個字符串做爲resultCode 
  • 經過遞歸調用負責調用堆棧中下一個Interceptor的執行 
  • 若是在堆棧內已經不存在任何的Interceptor,調用Action 


Struts2的攔截器結構的設計,其實是一個典型的責任鏈模式的應用。首先將整個執行劃分紅若干相同類型的元素,每一個元素具有不一樣的邏輯責任,並將他們歸入到一個鏈式的數據結構中(咱們能夠把堆棧結構也看做是一個遞歸的鏈式結構),而每一個元素又有責任負責鏈式結構中下一個元素的執行調用。 

這樣的設計,從代碼重構的角度來看,其實是將一個複雜的系統,分而治之,從而使得每一個部分的邏輯可以高度重用並具有高度可擴展性。因此,Interceptor結構實在是Struts2/Xwork設計中的精華之筆。數據結構

Interceptor執行分析 

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基礎。

相關文章
相關標籤/搜索