從軟件架構的角度講:攔截器屬於AOP編程的範疇。它將影響了多個業務對象的公共行為封裝到一個個可重用的模塊,減少了系統的重複代碼,實現功能的高度內聚,確保了業務對象                             的整潔和純度。

從java代碼的角度講:它就是一個普度的Java對象,它只需要實現一個名為Interceptor的接口。

當我們在struts.xml配置文件中包含struts-default包時,我們就會擁有默認的攔截器和攔截器棧。一個攔截器棧包含一組攔截器。堆棧中的每個攔截器使用interceptor-ref元素定義。

攔截器的工作原理:

當框架接收到一個請求的時候,它首先必須決定這個URL映射到哪一個動作組件。這個動作組件的一個實例就會被加入到一個新創建的ActionInvocation實例中。接着框架諮詢聲明性架構(XML配置文件或Java註解),以發現哪一些攔截器應該被觸發,以及按照什麼樣的順序觸發。將這些攔截器的引用添加到ActionInvocation中。除了這些核心元素,ActionInvocation也擁有其他重要的信息(Servlet請求對象和當前動作組件可用的結果組件列表)。

當ActionInvocation被創建完畢,並且填充了需要的所有對象和信息,就可以被使用了。ActionInvocation公開了一個invoke()方法,框架通過調用這個方法開始動作的執行。當框架調用了這個方法時,ActionInvocation通過執行攔截器棧中的第一個攔截器開始這個調用過程。需要注意的是,invoke()並不是總是指向攔截器棧的第一個攔截器,ActionInvocation負責跟蹤執行過程達到的狀態,並且把控制交給棧中合適的攔截器(通過調用攔截器的intercept()方法將控制權交給攔截器)。通過遞歸調用(遞歸過程?框架通過第一次調用ActionInvocation對象的invoke()方法開始了這個過程。ActionInvocation通過調用攔截器的intercept()方法把控制權交給第一個攔截器。重要的是,intercept()方法把ActionInvocation實例作為一個參數。在攔截器的處理過程中,他會調用ActionInvocation實例參數的invoke()方法來繼續調用後續攔截器的遞歸過程。因此,在通常的執行中,調用過程向下通過所有攔截器,直到棧中再也沒有攔截器時,觸發動作。另外,ActionInvocation在內部管理處理狀態,因此它總是直到自己現在處在棧的什麼位置。)ActionInvocation的invoke()方法,攔截器棧中後續的攔截器繼續執行,最終執行動作。這是因為每一次invoke()方法被調用時,ActionInvocation都會查詢自身的狀態,調用接下來的攔截器。在所有攔截器都被調用之後,invoke()方法會促使動作類執行。

執行順序:

在提交數據到框架時,框架調用攔截器的過程,首先框架會根據URL請求創建指定的動作TestAction,將TestAction的實例和TestAction相關的攔截器引用myStack放入一個新的ActionInvocation對象中(還包含其他信息),然後框架調用ActionInvocation的invoke()方法,此時開始了攔截器棧調用過程,最開始調用攔截器棧的第一個攔截器也就是Intercept1,攔截器執行完預處理後,因為intercept()方法接收一個ActionInvocation對象作為參數,在Intercept1.intercept()方法中繼續調用 ActionInvocation對象的invoke()方法將向下繼續調用棧中餘下的攔截器Intercept2...一直到棧中沒有攔截器為止,最後執行動作組件。在結果被呈現之後,攔截器會按照相反的順序再觸發一遍,使他們可以進行後處理。

攔截器工作原理圖:

GRpcExceptionHandlerInterceptor 使用_struts

攔截器觸發時能夠做些什麼?

1. 做一些預處理。在這個階段攔截器可以用來準備、過濾、改變或者操作任何可以訪問的重要數據。這些數據包括所有與當前請求相關的關鍵對象和數據,也包括動作。

2. 通過調用invoke()方法將控制轉移給後續的攔截器,直到動作。或者通過返回一個控制字符串中斷執行。在這個階段,如果攔截器決定請求不應該繼續,他可以不調用ActionInvocation實例上的invoke()方法,而是直接返回一個控制字符串。通過這種方式可以停止後續的執行,並且決定哪個結果被呈現。

3. 做一些後加工。在這個階段,任何一個返回的攔截器可以修改可以訪問的對象的數據作為後加工,但是此時結果已經確定了。

查看dtd文檔可以看到interceptors標籤要定義在package標籤下,我們對照struts-default.xml可以看到interceptors標籤確實定義在package標籤下,並且在dtd文檔中package元素右邊説明中interceptors標籤右邊帶有一個"?"符號,表示該標籤要麼有且只有1個,要麼沒有

再接着查看dtd文檔interceptors元素右面有"(interceptor|interceptor-stack)*"的一串字符式子,表示interceptors標籤下可以有0個或多個interceptor標籤和interceptor-stack標籤,並且不分順序

在dtd文檔中可以看到interceptor-stack的name屬性為#REQUIRED表示這是必須的屬性,你聲明一個攔截器棧必須給棧取一個名字。而interceptor標籤中有兩個屬性name和class它們都是#REQUIRED,所以你想定義一個攔截器就必須指明這兩個屬性的值,name為攔截器的名字,class為這個攔截器會使用哪一個類作為它的處理器。還有interceptor-stack標籤可以包含許多interceptor-ref標籤,這些標籤是用於引用你使用interceptor標籤聲明的攔截器另外的攔截器棧,它的name屬性的值為interceptor標籤聲明的攔截器的name值或其他攔截器棧的name值,該屬性也是#REQUIRED。

 最後一個package可以定義一組默認攔截器(有且只能有1個,dtd規定) ,例如在struts-default.xml文檔中

<default-interceptor-ref name="defaultStack"/>

定義了默認的攔截器組,這個默認的攔截器組會與這個包內沒有顯示聲明自己的攔截器的所有動作相關聯。 

下面為Struts 2為我們提供的struts-default.xml文件部分:

GRpcExceptionHandlerInterceptor 使用_攔截器_02

GRpcExceptionHandlerInterceptor 使用_struts_03

1 <struts>
  2         ...
  3         <interceptors>
  4             <interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
  5             <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>
  6             <interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/>
  7             <interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
  8             <interceptor name="cookie" class="org.apache.struts2.interceptor.CookieInterceptor"/>
  9             <interceptor name="clearSession" class="org.apache.struts2.interceptor.ClearSessionInterceptor"/>
 10             <interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor"/>
 11             <interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor"/>
 12             <interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/>
 13             <interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>
 14             <interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
 15             <interceptor name="i18n" class="com.opensymphony.xwork2.interceptor.I18nInterceptor"/>
 16             <interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/>
 17             <interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
 18             <interceptor name="scopedModelDriven" class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"/>
 19             <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
 20             <interceptor name="actionMappingParams" class="org.apache.struts2.interceptor.ActionMappingParametersInteceptor"/>
 21             <interceptor name="prepare" class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/>
 22             <interceptor name="staticParams" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/>
 23             <interceptor name="scope" class="org.apache.struts2.interceptor.ScopeInterceptor"/>
 24             <interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>
 25             <interceptor name="timer" class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/>
 26             <interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>
 27             <interceptor name="tokenSession" class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/>
 28             <interceptor name="validation" class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/>
 29             <interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/>
 30             <interceptor name="store" class="org.apache.struts2.interceptor.MessageStoreInterceptor"/>
 31             <interceptor name="checkbox" class="org.apache.struts2.interceptor.CheckboxInterceptor"/>
 32             <interceptor name="profiling" class="org.apache.struts2.interceptor.ProfilingActivationInterceptor"/>
 33             <interceptor name="roles" class="org.apache.struts2.interceptor.RolesInterceptor"/>
 34             <interceptor name="jsonValidation" class="org.apache.struts2.interceptor.validation.JSONValidationInterceptor"/>
 35             <interceptor name="annotationWorkflow" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor"/>
 36             <interceptor name="multiselect" class="org.apache.struts2.interceptor.MultiselectInterceptor"/>
 37 
 38             <!-- Basic stack -->
 39             <interceptor-stack name="basicStack">
 40                 <interceptor-ref name="exception"/>
 41                 <interceptor-ref name="servletConfig"/>
 42                 <interceptor-ref name="prepare"/>
 43                 <interceptor-ref name="checkbox"/>
 44                 <interceptor-ref name="multiselect"/>
 45                 <interceptor-ref name="actionMappingParams"/>
 46                 <interceptor-ref name="params">
 47                     <param name="excludeParams">dojo\..*,^struts\..*</param>
 48                 </interceptor-ref>
 49                 <interceptor-ref name="conversionError"/>
 50             </interceptor-stack>
 51 
 52             <!-- Sample validation and workflow stack -->
 53             <interceptor-stack name="validationWorkflowStack">
 54                 <interceptor-ref name="basicStack"/>
 55                 <interceptor-ref name="validation"/>
 56                 <interceptor-ref name="workflow"/>
 57             </interceptor-stack>
 58 
 59             <!-- Sample JSON validation stack -->
 60             <interceptor-stack name="jsonValidationWorkflowStack">
 61                 <interceptor-ref name="basicStack"/>
 62                 <interceptor-ref name="validation">
 63                     <param name="excludeMethods">input,back,cancel</param>
 64                 </interceptor-ref>
 65                 <interceptor-ref name="jsonValidation"/>
 66                 <interceptor-ref name="workflow"/>
 67             </interceptor-stack>
 68 
 69             <!-- Sample file upload stack -->
 70             <interceptor-stack name="fileUploadStack">
 71                 <interceptor-ref name="fileUpload"/>
 72                 <interceptor-ref name="basicStack"/>
 73             </interceptor-stack>
 74 
 75             <!-- Sample model-driven stack  -->
 76             <interceptor-stack name="modelDrivenStack">
 77                 <interceptor-ref name="modelDriven"/>
 78                 <interceptor-ref name="basicStack"/>
 79             </interceptor-stack>
 80 
 81             <!-- Sample action chaining stack -->
 82             <interceptor-stack name="chainStack">
 83                 <interceptor-ref name="chain"/>
 84                 <interceptor-ref name="basicStack"/>
 85             </interceptor-stack>
 86 
 87             <!-- Sample i18n stack -->
 88             <interceptor-stack name="i18nStack">
 89                 <interceptor-ref name="i18n"/>
 90                 <interceptor-ref name="basicStack"/>
 91             </interceptor-stack>
 92             ...
 93 
 94        </interceptors>
 95 
 96         <default-interceptor-ref name="defaultStack"/>
 97 
 98         <default-class-ref class="com.opensymphony.xwork2.ActionSupport"/>
 99     </package>
100 
101 </struts>

View Code

如何定義攔截器:實現Interceptor接口;創建自定義的攔截器還可以擴展com.opensymphony.xwork2.interceptor.AbstractInterceptor類,它並沒有什麼高級的地方,它僅僅只是幫我們實現了Interceptor接口,幫我們默認實現了init()和destory()方法

下面演示一個簡單的權限驗證的例子:

它的工作原理很簡單,當一個請求訪問一個安全動作時,我們想檢查請求是否是一個通過身份驗證的用户發出的。

權限認證攔截器:

GRpcExceptionHandlerInterceptor 使用_攔截器_02

GRpcExceptionHandlerInterceptor 使用_struts_03

1 public class AuthenticationInterceptor implements Interceptor
 2 {
 3     private static final long serialVersionUID = -1500368808387165682L;
 4 
 5     public void destroy()
 6     {
 7 
 8     }
 9 
10     public void init()
11     {
12 
13     }
14 
15     public String intercept(ActionInvocation invocation) throws Exception
16     {
17         Map<String , Object> session = invocation.getInvocationContext().getSession();
18 
19         User user = (User) session.get("USER");
20 
21         if (user == null)
22         {
23             return Action.INPUT;
24         }
25         else
26         {
27             Action action = (Action) invocation.getAction();
28             if (action instanceof UserAware)
29             {
30                 ((UserAware) action).setUser(user);
31             }
32         }
33 
34         return invocation.invoke();
35     }
36 }

View Code

最後,Struts 2內置了很多的日常Web開發都會用到的攔截器,所以一般不太需要自己開發一個攔截器,內置的攔截器幾乎包含了所有日常需要的功能,要想真正運用好Struts 2必須要了解他們的工作原理,在Struts 2的官方網站上有詳細的介紹,可以去哪裏找到你需要的東西。