struts2 攔截器詳解

攔截器,用於在某個方法或字段被訪問之前,進行攔截然後在之前或之後加入某些操作。
    攔截器是AOP(Aspect-Oriented Programming 面向方面編程)中的一種實現策略。
    1 用代理模式與動態代理編寫攔截器
      
       所謂Dynamic Proxy是這樣一種class:它是在運行時生成的class,在生成它時你必須提供一組interface給它,
       然後該class就宣稱它實現了這些interface。你當然可以把該class的實例當作這些interface中的任何一個來用。
       當然啦,這個Dynamic Proxy其實就是一個Proxy,它不會替你作實質性的工作,
       在生成它的實例時你必須提供一個handler,由它接管實際的工作
        那麼如何實現動態代理呢
        1 寫一個動態代理,implements InvocationHandler接口,實現方法
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                  
                   method.invoke(sub,args);
                return null;
                }
        2 調用 ,
        真實實現類名 xxx = new 真實實現類名(); // 在這裏指定被代理類
        InvocationHandler ds = new DynamicSubject(xxx); // 初始化代理類
        接口  接口變量= (接口) Proxy.newProxyInstance(xxx.getClass().getClassLoader(), xxx.getClass().getInterfaces(), ds);
                       接口變量.方法
          
               /**
         * 從以上可以看出,動態代理可以任意指定被代理的類,即真實對象類,而代理模式則無法實現該功能,
         * 因爲他把真實對象的寫死在代理類裏,如果要按照上述的方法使用代理模式,那麼真實角色必須是事先已經存在的,並將其作爲代理對象的內部屬性。但是
         * 實際使用時,一個真實角色或其接口必須對應一個代理角色,如果大量使用會導致類的急劇膨脹
         */

      
    2  struts2攔截器
      解壓strut2的核心包,根目錄下struts-default.xml中有struts2的攔截器配置
    

       攔截器幾乎完成了Struts2框架70%的工作,包括解析請求參數、將請求參數賦值給Action屬性、執行數據校驗、
       文件上傳……,Struts2設計的靈巧性,更大程度地得益於攔截器設計,當需要擴展Struts2功能時,只需要提供對應攔截器,
       並將它配置在Struts2容器中即可;如果不需要該功能時,也只需要取消該攔截器的配置即可。
       這種可插拔式的設計,正是軟件設計領域一直孜孜以求的目標。
    實際上,Struts2的精髓就在於攔截器,掌握了Struts2的攔截器機制,你就可以說精通了Struts2。
    從某個角度來看,我們可以把Struts2框架理解成一個空殼,而這些攔截器像一個一個抽屜,隨時可以
    插進入,也可以拔出來——這是軟件產品一直追求的目標。
    如果你喜歡,你可以把Struts2的全部插件拔出,那麼Struts2就成了一個空容器——
    而這種空,正是 Struts2的魅力,你可以把任何自己想要的東西填入進去,甚至包括自己完全實現這個框架。

    另一方面,因爲Struts2的插件機制,Struts2提供了無限擴展的可能性,你可以把自己想要的任何
    東西做成插件,然後填入Struts2——這樣的結果是:一個企業,一個團隊,可以把自己業務相關的東西
    做成插件,隨時隨地地複用。
    也就是說:如果你想要,你可以把Struts2改造成屬於自己的框架。

    當然,Struts2也內建了大量的攔截器,這些攔截器以name-class對的形式配置在struts-default. xml文件中,其中name是攔截器的名字,就是以後使用該攔截器的唯一標識;class則指定了該攔截器的實現類,如果我們定義的package繼承了Struts2的默認struts-default包,則可以自由使用下面定義的攔截器,否則必須自己定義這些攔截器。
    下面是Struts2內建攔截器的簡要介紹:
    alias:實現在不同請求中相似參數別名的轉換。
    autowiring:這是個自動裝配的攔截器,主要用於當Struts2和Spring整合時,Struts2可以使用自動裝配的方式來訪問Spring容器中的Bean。
    chain:構建一個Action鏈,使當前Action可以訪問前一個Action的屬性,一般和<result type="chain" .../>一起使用。
    conversionError:這是一個負責處理類型轉換錯誤的攔截器,它負責將類型轉換錯誤從ActionContext中取出,並轉換成Action的FieldError錯誤。
    createSession:該攔截器負責創建一個HttpSession對象,主要用於那些需要有HttpSession對象才能正常工作的攔截器中。
    debugging:當使用Struts2的開發模式時,這個攔截器會提供更多的調試信息。
    execAndWait:後臺執行Action,負責將等待畫面發送給用戶。
    exception:這個攔截器負責處理異常,它將異常映射爲結果。
    fileUpload:這個攔截器主要用於文件上傳,它負責解析表單中文件域的內容。
    i18n:這是支持國際化的攔截器,它負責把所選的語言、區域放入用戶Session中。
    logger:這是一個負責日誌記錄的攔截器,主要是輸出Action的名字。
    model-driven:這是一個用於模型驅動的攔截器,當某個Action類實現了ModelDriven接口時,它負責把getModel()方法的結果堆入ValueStack中。
    scoped-model-driven:如果一個Action實現了一個ScopedModelDriven接口,該攔截器負責從指定生存範圍中找出指定的Modol,並將通過setModel方法將該Model傳給Action實例。
    params:這是最基本的一個攔截器,它負責解析HTTP請求中的參數,並將參數值設置成Action對應的屬性值。
    prepare:如果action實現了Preparable接口,將會調用該攔截器的prepare()方法。
    static-params:這個攔截器負責將xml中<action>標籤下<param>標籤中的參數傳入action。
    scope:這是範圍轉換攔截器,它可以將Action狀態信息保存到HttpSession範圍,或者保存到ServletContext範圍內。
    servlet-config:如果某個Action需要直接訪問Servlet API,就是通過這個攔截器實現的。
    注意:儘量避免在Action中直接訪問Servlet API,這樣會導致Action與Servlet的高耦合。
    roles:這是一個JAAS(Java Authentication and Authorization Service,Java授權和認證服務)攔截器,只有當瀏覽者取得合適的授權後,纔可以調用被該攔截器攔截的Action。
    timer:這個攔截器負責輸出Action的執行時間,這個攔截器在分析該Action的性能瓶頸時比較有用。
    token:這個攔截器主要用於阻止重複提交,它檢查傳到Action中的token,從而防止多次提交。
    token-session:這個攔截器的作用與前一個基本類似,只是它把token保存在HttpSession中。
    validation:通過執行在xxxAction-validation.xml中定義的校驗器,從而完成數據校驗。
    workflow:這個攔截器負責調用Action類中的validate方法,如果校驗失敗,則返回input的邏輯視圖。
    大部分時候,開發者無需手動控制這些攔截器,因爲struts-default.xml文件中已經配置了這些攔截器,只要我們定義的包繼承了系統的struts-default包,就可以直接使用這些攔截器。

    當然,Struts2的攔截器機制並不是來自於Struts1,而是來自於webWork。




  3  自定義攔截器
     (1)編寫攔截器類,繼承AbstractInterceptor類 重寫intercept(ActionInvocation arg0)方法
          調用用參數類ActionInvocation的invoke方法,即 String result= arg0.invoke();  返回該result=
          invoke就是回調使用了該攔截器的action得相應方法,此時可在該方法執行前後加入我們想要的代碼,達到我們攔截action的目的
      利用 arg0.getAction()方法還可以得到攔截器攔截的action實例
    public String intercept(ActionInvocation arg0) throws Exception {
     //  LoginAction loginaction=LoginAction(arg0.getAction());
       System.out.println("執行ction之前");
      String result= arg0.invoke();
       System.out.println("執行ction之後");
        return result;
    }    
    、(2)在struts.xml配置攔截器
        <package name="default" extends="struts-default" namespace="/">    
    <!--配置攔截器  -->
    <interceptors>
    <interceptor name="攔截器名" class="包名.攔截器類名">
        <!--設置攔截器類屬性值,如沒有則不用設置  -->
          <param name="攔截器類屬性名">屬性值</param>
    </interceptor>
    </interceptors>
        <action name="login"  class="com.LoginAction" >
        <result name="success">/welcome.jsp</result>
        <result name="error">/error.jsp</result>
        <interceptor-ref name="defaultStack"></interceptor-ref><!--struts-default包默認的攔截器棧  -->
            <!--使用攔截器  -->
        <interceptor-ref name="攔截器名"></interceptor-ref>
        </action>
        
    </package>

     多個攔截器在一起組成一個攔截器棧
          <interceptor-stack name="攔截器棧">
                <interceptor-ref name="攔截器名1"/>
                <interceptor-ref name="攔截器名2"/>
                <interceptor-ref name="攔截器名3"/>
            </interceptor-stack>

比如:<package name="default" extends="struts-default">
        <interceptors>
            <interceptor name="MyInterceptor" class="com.lxitedu.test.InterceptorCustom"></interceptor>
        </interceptors>
        
        <action name="register" class="com.lxitedu.test.Register">
            <interceptor-ref name="MyInterceptor"></interceptor-ref>
            <interceptor-ref name="defaultStack" />
            <result name="fail">/index.jsp </result>
            <result name="success">/success.jsp</result>
        </action>
    </package>



    使用攔截器棧 與 使用攔截器一樣語法

     當配置一個包時,可以爲其指定默認的攔截器,每個包只能有一個默認的攔截器,一旦爲某個包指定了默認的攔截器,
     如果該包中的action沒有指定自己的攔截器,則action使用包指定的默認攔截器,但是一旦爲action指定了自己攔截器,
     則包的默認攔截器將會失效,如果還想使用原來包的默認攔截器,則必須顯示的指定,如
      <action name="login"  class="com.LoginAction" >
        <interceptor-ref name="defaultStack"></interceptor-ref><!--struts-default包默認的攔截器棧  -->
            <!--使用我們的攔截器  -->
        <interceptor-ref name="攔截器名"></interceptor-ref>
        </action>

     我們的包繼承另一個包時,也繼承了另一個包的默認攔截器,以及包裏面的其他如(<result-types>)
     當然我們可以定義自己包的默認攔截器覆蓋之


     從這裏可以解釋爲什麼我們剛剛開始學struts2的時候老是extends="struts-default"
     因爲struts-default包中的默認攔截器以及result-types是個好東東
     如果不繼承它,將失去result-types以及裏面定義的攔截器功能,可以試着extends="struts-default"去掉,此時如果struts.xml裏有
     result的配置的話,則tomcat啓動的時候No result type specified for result named 。。。。。錯誤。我們可以把裏面的 result配置去掉
     重新啓動tomcat,去測試頁面屬性值是否能夠封裝到action屬性中,答案不會 因爲我們沒有繼承struts-default
    
 



     配置默認攔截器 在 包package中 <default-interceptor-ref name="攔截器名或攔截器棧"></default-interceptor-ref> 放後面,action配置的前面
      
     
        上面攔截器會攔截action中的所有方法 要想攔截某個方法怎麼辦  攔截指定方法  
      編寫攔截器類,extends MethodFilterInterceptor   MethodFilterInterceptor是AbstractInterceptor的子類
      重寫doIntercept方法
         public String doIntercept(ActionInvocation invocation)
        throws Exception
    {
         //  LoginAction loginaction=LoginAction(arg0.getAction());
           System.out.println("執行action方法之前");
           String result= arg0.invoke();
          System.out.println("執行action方法之後");
        return result;
    }
     
       使用配置指定攔截的方法
         <!-- 攔截器一般配置在result元素之後! -->
            <interceptor-ref name="攔截器名1">
                <param name="excludeMethods">execute,haha</param>         <!-- 不攔截! -->
                <param name="includeMethods">execute</param>               <!-- 攔截! -->
            </interceptor-ref>


      可以重複使用一個攔截器或配置多個攔截器與攔截器棧,攔截器的執行順序是在方法執行前,先配先執行,在方法執行後,後配先執行


      攔截結果的監聽 是在action結束後,返回result之前的一個監聽器,可以在該監聽器裏寫我們的代碼,
      以便在返回結果前執行,
      這個監聽器通過手動註冊在攔截器內部的
      監聽器示例代碼  implements PreResultListener
        public class MyPreResultListener implements PreResultListener
         {
    public void beforeResult(ActionInvocation invocation,String resultCode)
    {
        System.out.println("返回的邏輯視圖爲:" + resultCode);
    }
      }

         攔截器示例代碼
        public class BeforeResultInterceptor extends AbstractInterceptor
           {
        public String intercept(ActionInvocation invocation) throws Exception
        {
            invocation.addPreResultListener(new MyPreResultListener()); //註冊監聽器
            System.out.println("execute方法執行之前的攔截...");
            String result = invocation.invoke();
            System.out.println("execute方法執行之後的攔截......");
            return result;

          }
           }

     監聽器代碼在action結束後,返回結果前執行,和在action結束後在攔截器內部寫的代碼相比, 監聽器代碼把action結束後的代碼
     放到監聽器似乎更精確和清晰些

       

     給攔截器棧傳遞參數時會出現一個問題,當攔截器棧中攔截器類中的屬性名相同時,不知道這個參數到底要傳給那個攔截器,爲了
     解決這個問題,可作如下配置
            <interceptor-ref name="攔截器棧名">
                    <param name="攔截器名(不是類名,注意哦).屬性名">屬性值</param>
            </interceptor-ref>
發佈了31 篇原創文章 · 獲贊 1 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章