Struts2攔截器原理以及實例


Struts2攔截器原理以及實例

 

一、Struts2攔截器定義

1. Struts2攔截器是在訪問某個Action或Action的某個方法,字段之前或之后實施攔截,並且Struts2攔截器是可插拔的,攔截器是AOP的一種實現.

2. 攔截器棧(Interceptor Stack)。Struts2攔截器棧就是將攔截器按一定的順序聯結成一條鏈。在訪問被攔截的方法或字段時,Struts2攔截器鏈中的攔截器就會按其之前定義的順序被調用。

二、實現Struts2攔截器原理

  Struts 2的攔截器實現相對簡單。當請求到達Struts2的ServletDispatcher時,Struts 2會查找配置文件,並根據其配 置實例化相對的攔截器對象,然后串成一個列表(list),最后一個一個地調用列表中的攔截器。事實上,我們之所以能夠如此靈活地使用攔截器,完全歸功於“動態代理”的使用。動態代理是代理對象根據客戶的需求做出不同的處理。對於客戶來說,只要知道一個代理對象就行了。

那Struts2中,攔截器是如何通過動態代理被調用的呢? 當Action請求到來的時候,會由系統的代理生成一個Action的代理對象,由這個代理對象調用Action的execute()或指定的方法,並在 struts.xml中查找與該Action對應的攔截器。如果有對應的攔截器,就在Action的方法執行前(后)調用這些攔截器;如果沒有對應的攔截 器則執行Action的方法。其中系統對於攔截器的調用,是通過ActionInvocation來實現的。代碼如下:

復制代碼
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()); 
} }
復制代碼

三.攔截器執行分析

     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。

三.源碼解析

        通 過查看源碼來看看Struts2是如何保證攔截器、Action與Result三者之間的執行順序的。之前我曾經提到,ActionInvocation 是Struts2中的調度器,所以事實上,這些代碼的調度執行,是在ActionInvocation的實現類中完成的,這里,我抽取了 DefaultActionInvocation中的invoke()方法,它將向我們展示一切。

復制代碼
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)、Action、PreResultListener和Result。在這個方法中,保證了這些層次的有序調用和執行。由此我們也可以看出Struts2在Action層次設計上的眾多考慮,每個層次都具備了高度的擴展性和插入點,使得程序員可以在任何喜歡的層次加入自己的實現機制改變Action的行為。
在這里,需要特別強調的,是其中攔截器部分的執行調用:

resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this); 

原 來在intercept()方法又對ActionInvocation的invoke()方法進行遞歸調用,ActionInvocation循環嵌套在 intercept()中,一直到語句result = invocation.invoke()執行結束。這樣,Interceptor又會按照剛開始 執行的逆向順序依次執行結束。一個有序鏈表,通過遞歸調用,變成了一個堆棧執行過程,將一段有序執行的代碼變成了2段執行順序完全相反的代碼過程,從而巧妙地實現了AOP。這也就成為了Struts2的Action層的AOP基礎。 

攔截器和過濾器之間有很多相同之處,但是兩者之間存在根本的差別。其主要區別為以下幾點:
1)攔截器是基於JAVA反射機制的,而過濾器是基於函數回調的。
2)過濾器依賴於Servlet容器,而攔截器不依賴於Servlet容器
3)攔截器只能對Action請求起作用,而過濾器可以對幾乎所有的請求起作用。
4)攔截器可以訪問Action上下文、值棧里的對象,而過濾器不能
5)在Action的生命周期中,攔截器可以多次被調用,而過濾器只能在容器初始化時被調用一次。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM