struts2重新梳理---Struts2攔截器

 

 

攔截器(Interceptor)是Struts2的核心和基礎,有許多功能都是構建於它之上,如國際化、轉換器、校驗等。

截攔器是AOP的一種實現,底層通過動態代理模式完成

 

struts2攔截器初識

        客戶端向Web應用發送HttpServletRequest請求,請求經過各種過濾器的過濾並傳遞給核心控制器FilterDispatcher;
          FilterDispatcher會調用Action映射器ActionMapper,將用戶請求轉發到對應的業務邏輯控制器,此業務邏輯控制器並不是用戶實現的業務控制器,而是Struts2創建的      Action代理ActionProxy,用戶實現的Action類僅僅是Struts 2的ActionProxy的代理目標;
  ActionProxy通過Configuration Manager在struts.xml配置文件中搜索被請求的Action類。 ActionProxy創建一個被請求Action的實例,用來處理客戶端請求信息;
  如果在struts.xml配置文件存在於被請求Action相關的攔截器配置,那麼該Action實例被調用的前後,這些攔截器將會被執行;
   Action對請求處理完畢後返回一個邏輯視圖,由此邏輯視圖找到相應物理視圖(可以說JSP、Velocity模板、FreeMarker模板等)返回給客戶端。
   用戶實現的Action類僅僅是Struts 2的ActionProxy的代理目標;用戶的Action類無需訪問HttpServletRequest對象——因爲用戶實現的業務控制器並沒有與Servlet API耦合,但用戶的請求數據又是包含在HttpServletRequest對象裏,怎麼辦呢?
    Struts 2框架則提供了系列攔截器,該系列攔截器負責將HttpServletRequest請求中的請求參數解析出來,傳入到Action中,並回調Action的execute方法來處理用戶請求
    攔截器可以讓你在Action被執行之前或之後進行一些處理。同時,攔截器也可以讓你將通用的代碼模塊化並作爲可重用的類。
 
     官方定義:攔截器是動態攔截Action調用的對象。它提供了一種機制可以使開發者可以定義一段代碼,使其在一個Action執行之前或之後被調用執行,也可以在一個Action執行前阻止其執行。同時也是提供了一種可以提取Action中可重用部分的方式。
    簡單地說:攔截器(Interceptor)是一個實現了一定功能的類,以一種可插拔的方式被定義在某個Action執行的之前或之後,用來完成特定的功能。當爲Action添加該功能的時候就配置該攔截器,當爲Action減少該功能的時候就取消配置該攔截器。
    攔截器棧(Interceptor Stack,在WebWork中稱爲攔截器鏈Interceptor Chain)。攔截器棧就是一堆攔截器的集合體,它將這些攔截器按一定的順序聯結成一條鏈。在訪問被攔截的方法或字段時,攔截器棧中的攔截器就會按其之前定義的順序被調用。
 
攔截器原理
•ActionInvocation是Action調度者,invoke()是ActionInvocation中的方法,所以這個方法具備以下兩層含義:
1. 如果攔截器堆棧中還有其他的Interceptor,那麼invoke()將調用堆棧中下一個Interceptor的執行。
2. 如果攔截器堆棧中只有Action了,那麼invoke()將調用Action執行。
 

圖中,Struts2的Interceptor一層一層,把Action包裹在最裏面。這樣的結構,大概有以下一些特點:

   整個結構就如同一個堆棧,除了Action以外,堆棧中的其他元素是Interceptor
 Action位於堆棧的底部。由於堆棧“先進後出”的特性,如果我們試圖把Action拿出來執行,我們必須首先把位於Action上端的Interceptor拿出來執行。這樣,整個執行就形成了一個遞歸調用。
   每個位於堆棧中的Interceptor,除了需要完成它自身的邏輯,還需要完成一個特殊的執行職責。這個執行職責有3種選擇:
1) 中止整個執行,直接返回一個字符串作爲resultCode
2) 通過遞歸調用負責調用堆棧中下一個Interceptor的執行
3) 如果在堆棧內已經不存在任何的Interceptor,調用Action
 
 
    Struts 2的攔截器實現相對簡單。當請求Struts 2的某Action時,Struts 2會查找配置文件,並根據配置實例化其相對應的攔截器對象,然後串成一個列表(list),最後一個一個地調用列表中的攔截器。
 
 
  Struts2的攔截器結構的設計,實際上是一個典型的責任鏈模式的應用。首先將整個執行劃分成若干相同類型的元素,每個元素具備不同的邏輯責任,並將它們納入到一個鏈式的數據結構中(我們可以把堆棧結構也看作是一個遞歸的鏈式結構),而每個元素又有責任負責鏈式結構中下一個元素的執行調用。
   這樣的設計,從代碼重構的角度來看,實際上是將一個複雜的系統,分而治之,從而使得每個部分的邏輯能夠高度重用並具備高度可擴展性。所以,Interceptor結構實在是Struts2/Xwork設計中的精華之筆。
  struts2內置攔截器
 
    Struts2中內置類許多的攔截器,它們提供了許多Struts2的核心功能和可選的高級特性。這些內置的攔截器在struts-default.xml中配置。
 
 
配置和使用攔截器
 
  在struts.xml中部署攔截器,通過<interceptors> </interceptors> 標籤對內使用<interceptor/> 標籤引入具體攔截器。
  部署好攔截器後,就可以在<action> </action>標籤對內使用<interceptor-ref…/> 標籤引入所需攔截器爲Action應用添加相應的功能了。
   需要注意的是,如果爲Action指定了一個攔截器,則系統默認的攔截器棧將會失去作用,導致Struts2無法正常運行,爲了繼續使用默認攔截器,所以需在配置文件中手動引入默認攔截器。
  使用<param> </param> 標籤對可爲攔截器指定其所需參數。】
 

    <package name="default" extends="struts-default">  

   <interceptors>  

        <interceptor name=“攔截器名1” class=“攔截器實現類1"/> 

        <interceptor name=“攔截器名2” class=“攔截器實現類2”>

           < param name= “參數名” >參數值</param>

   </interceptor>

        // …………其他攔截器

   </interceptors>

   <action name=“Action名” class=“Action類">  

  <interceptor-ref name="defaultStack"/>

        <interceptor-ref name=“攔截器名1”/>

        <interceptor-ref name="攔截器名2">  

        < param name= “參數名” >參數值</param>  

  </interceptor-ref>

  // …………Action其他配置

   </action>

</package>

配置和使用攔截棧
 
  將多個截攔器組合成一個截攔器棧
 攔截器棧部署與攔截器略有不同,一個攔截器棧可以包括一個或多個攔截器,也可以包括其他攔截器棧。通過<interceptor-stack> </ interceptor-stack> 標籤部署攔截器棧。
 與使用攔截器一樣,在<action> </action>標籤對內使用<interceptor-ref…/> 標籤引入所需攔截器棧爲Action應用添加相應的功能。
 

    <package name="default" extends="struts-default">  

   <interceptors>  

        <interceptor name=“攔截器名1” class=“攔截器實現類1"/> 

        <interceptor name=“攔截器名2” class=“攔截器實現類2”/>

  <interceptor name=“攔截器名3” class=“攔截器實現類3”/>

  <interceptor name=“攔截器名3” class=“攔截器實現類4”/>

  // …………其他攔截器

  <interceptor-stack name=“myStack1”> //自定義攔截器棧1

               <interceptor-ref name=“攔截器名1”/>

     <interceptor-ref name="defaultStack"/>

        </interceptor-stack> 

  <interceptor-stack name=“myStack2”> //自定義攔截器棧2

               <interceptor-ref name=“攔截器名2”/>

               <interceptor-ref name=“攔截器名3”/>

        </interceptor-stack> 

   </interceptors>

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

   <action name=“Action名” class=“Action類">  

  <interceptor-ref name=“myStack2"/>

        <interceptor-ref name=“攔截器名4”/>

  // …………Action其他配置

   </action>

</package>

    修改

      package的默認攔截器會應用到package中的所有Action。

自定義攔截器
 

        做爲“框架(framework)”,可擴展性是不可或缺的。雖然,Struts 2爲我們提供了豐富的攔截器實現,但是這並不意味我們失去創建自定義攔截器的能力,恰恰相反,在Struts 2自定義攔截器非常容易。

自定義攔截器
攔截器對Action中方法進行攔截
自定義攔截器及其使用:
1.直接或間接實現接口com.opensymphony.xwork2.interceptor.Interceptor  
2.或者繼承類com.opensymphony.xwork2.interceptor.AbstractInterceptor
3.通過<interceptor>元素來定義攔截器
4.通過<interceptor-ref>元素來使用攔截器
 
Interceptor接口中定義了三個方法:
void init():在攔截器初始化之後,在執行攔截之前,系統調用該方法。對於一個攔截器而言,init方法只會被調用一次。
String intercept(ActionInvocation invocation) throws Exception:該方法是攔截器的攔截方法,返回一個字符串,系統將會跳轉到該字符串對應的視圖資源。該方法的ActionInvocation參數包含了被攔截的Action的引用,可以通過該對象的invoke方法,將控制權轉給下一個攔截器或者轉給Action的execute方法。
void destroy():該方法與init方法對應,在攔截器示例被銷燬之前,系統將會調用該方法。

public class CustomInterceptor1 implements Interceptor {

  public String intercept(ActionInvocation invocation) throws Exception {

  /*Action前置攔截

   */

  System.out.println(“========我是一個攔截器========”);

  String result = invocation.invoke();

  /*Action後置攔截

   */

  System.out.println("========我是一個攔截器========");

  return result;

  }

}

•以invocation.invoke()爲界,將攔截器中的代碼分成2個部分,在invocation.invoke()之前的代碼,將會在Action之前被依次執行,而在invocation.invoke()之後的代碼,將會在Action之後被逆序執行。
 
 所有的Struts 2的攔截器都直接或間接實現接口com.opensymphony.xwork2.interceptor.Interceptor。
 除了Interceptor接口外,Struts2中還提供了一個AbStractInterceptor類,該類提供了一個init和destroy方法的空實現。如果不需要就不用重寫這兩個方法,可見繼承AbstractInterceptor類可以讓我們構建攔截器時變得更簡單。
    攔截器對Action中的方法進行攔截
     某Action添加攔截器後,則該Action中所有的方法都要受到此攔截器的影響,有時候並不是所有的方法都需要添加攔截器功能,此時可以通過設置相應參數來對Action中的某些方法指定攔截器。
Struts2提供MethodFilterInterceptor類,該類是AbstractInerceptor的子類,可以實現對Action方法的攔截.
自定義攔截器時必須繼承MethodFilterinterceptor,它接受兩個參數:
excludeMethods:設置不會被攔截器所攔截的方法,多個方法間用逗號隔開
includeMethods:設置被攔截器攔截的方法,多個方法間用逗號隔開
 
對運行時間較長的Action  視圖界面的處理
通過executeandwait攔截器來解決請求處理時間過長的問題。注意此攔截器必須放在所有攔截器的最後。
1、增加攔截器
     <interceptor-ref name="execAndWait">
       <!-- 等待時間,執行時間沒有超過此值,將不顯示等待畫面 (毫秒)-->
            <param name="delay">1000</param>
            <!-- 間隔檢查時間,檢查後臺進程有沒有執行完畢,如果完成了它就立刻返回,不用等到等待,用戶不會看到等待畫面 -->
            <param name="delaySleepInterval">50</param>
       </interceptor-ref>
includeMethods比excludeMethods優先級高
2、增加result
       <result name=“wait”>wait.jsp</result>
如果沒有找到“wait”結果,WebWork會自動生成一個wait結果(\com\opensymphony\webwork\interceptor\wait.ftl).這個結果是用FreeMarker做的,所以需要Freemarker支持才能正常工作。
如果你不想在程序中加入FreeMarker,那就必須自己實現一個wait結果。這一般來說是有必要的,因爲默認的wait頁面很簡單。
 
重複提交的解決辦法
 
一、使用tokenInterceptor攔截器
1、在提交頁面如登錄頁面中加入token標記<s:token></s:token>

2、在struts.xml 中配置tokenInterceptor攔截器和配置invoke.token信息。
      <result name="success">/login/welcome.jsp</result>
      <result name="invalid.token">/login/wrong.jsp</result>
      <interceptor-ref name="defaultStack"/>
      <!-- 配置token攔截器-->       
      <interceptor-ref name="token"/>
二、使用tokenSessionInterceptor攔截器  
1、在提交頁面如登錄頁面中加入token標記<s:token></s:token>
2、在struts.xml 中配置tokenInterceptor攔截器:
     <interceptor-ref name= "tolenSession"/>
      當檢查到重複提交後,總會返回到第一次成功提交的頁面。
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章