詳解Struts2攔截器機制

?????Struts2的核心在于它復雜的攔截器,幾乎70%的工作都是由攔截器完成的。比如我們之前用于將上傳的文件對應于action實例中的三個屬性的fileUpload攔截器,還有用于將表單頁面的http請求參數設置成action中對應的屬性的param攔截器等。總之,在整個Struts框架中攔截器的作用是相當大的,本篇將從以下幾點詳細介紹下有關Struts攔截器的內容:

  • 攔截器在Struts中的作用
  • 自定義攔截器實現類
  • 配置攔截器(包含配置默認攔截器)
  • 引用攔截器
  • 配置攔截指定方法的攔截器
  • 攔截器的攔截順序

一、攔截器在Struts中的作用
?????在我們的web.xml中,我們配置了一個過濾器,實現將所有請求交付StrutsPrepareAndExecuteFilter類。一旦接受到任意action的請求,該類會創建和初始化一個ActionProxy實例,它代理了具體的action,在其中我們可以添加任意攔截器在execute方法執行之前和之后做一些額外的操作,最終會調用該action實例的execute方法,為用戶返回視圖結果字符串,然后系統會根據該視圖結果字符串調取相應的視圖頁面。下圖是攔截器和action之間的關系:

這里寫圖片描述

這是一種典型的AOP思想,當我們在Struts.xml中定義一個包的時候,大部分情況下我們會繼承struts-default文件,所以雖然我們在自己的配置文件中并沒有手動配置任何的攔截器,但是我們創建的action卻被很多攔截器攔截處理,就是因為struts-default中配置的攔截器生效了。Struts中內建了很多的攔截器,他們大多被配置在struts-default文件中,詳細的內建攔截器的介紹可以參考官方API,接下來我們看如何自定義一個攔截器。

二、自定義攔截器實現類
?????想要實現自己的攔截器類只需要實現 com.opensymphony.xwork2.interceptor.Interceptor.Interceptor 接口即可,該接口中有如下幾個方法:

  public abstract void destroy();
  
  public abstract void init();
  
  public abstract String intercept(ActionInvocation paramActionInvocation)
    throws Exception;

init 方法在執行攔截方法之前回調,主要用于初始化一些資源,destroy 與init 方法對應,在攔截器實例被銷毀之前回調,主要用于釋放在init 方法中打開的資源。intercept 方法是我們的攔截方法,我們可以重寫該方法來完成對action實例的攔截,該方法具有一個ActionInvocation 類型的參數,該參數內部引用了具體的action實例對象(如果該action還有其他攔截器的話),我們可以調用該參數的invoke方法調用具體action實例的execute方法或者調用下一個攔截器,intercept方法返回一個String 類型的字符串代表了具體視圖頁面。下面看個具體的例子:

public class TestAction extends ActionSupport {

    public String execute(){
        System.out.println("執行execute方法......");
        return SUCCESS;
    }
}
public class MyIntercept implements Interceptor {

    public void init() {}
    public void destroy() {}

    public String intercept(ActionInvocation action) throws Exception{
        System.out.println("攔截action開始.......");
        String result = action.invoke();
        System.out.println("攔截action結束.......");
        return result;
    }
}

省略了配置攔截器和TestAction 的代碼,下圖是上述程序運行的結果截圖:

這里寫圖片描述

三、配置和引用攔截器
?????上述的示例定義了一個簡單的攔截器實現類,我們省略了在struts.xml中配置和引用該攔截器的代碼,本小節將詳細的介紹如何在struts.xml中定義和引用我們自定義實現的攔截器類。

從struts-default.xml中我們可以看出來,我們使用<interceptors>元素定義攔截器name和物理位置的配對,例如:

<interceptors>
    <interceptor name="test" class="MyPackage.TestAction"/>
    ......
    ......
</interceptors>

上述代碼定義了一個攔截器test,它對應于具體的一個class。需要注意的是,定義攔截器的元素 interceptors 及其子元素必須被配置在某個package包下。

以上只是定義了一個攔截器和具體攔截器實現類之間的映射關系,但是想要實現對某個具體的action的攔截需要使用元素<interceptor-ref>根據name屬性值引用一個上述已經定義了的攔截器。例如:

<action name="test" class="MyPackage.TestAction">
    <interceptor-ref name="test"/>
    <result name="success">/index.jsp</result>
    ......
    ......
</action>

正如上述代碼展示的一樣,該元素用于引用一個已經定義好了的攔截器,并且該元素出現在具體的action內部,表明了該action具有一個test攔截器。以上代碼實現了對單個攔截器的定義和引用,其實對于攔截器棧(一堆攔截器的組合)來說配置也是類似的。定義一個攔截器棧的代碼是如下的:

<interceptor-stack name="攔截器棧名">
    interceptor-ref name="攔截器一"/>
    interceptor-ref name="攔截器二"/>
    interceptor-ref name="攔截器三"/>
    .....
</interceptor-stack>

引用一個攔截器棧就沒什么區別了:

interceptor-ref name="攔截器棧名"/>

當然我們也可以通過

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

配置默認攔截器或者攔截器棧,如果該包下某個action沒有顯式指定攔截器,那么就會調用該默認攔截器,否則如果顯式配置了攔截器,那么默認攔截器將會失效。

四、為Action中指定方法配置攔截器
?????在默認情況下,我們為action配置了攔截器之后,該攔截器將會攔截該action中所有的方法,這有時候會給我們帶來麻煩,當然struts為我們提供API用來針對具體的某個方法配置攔截器。這里涉及到一個抽象類:MethodFilterInterceptor。該類實際上實現了Interceptor并完成了一些默認實現,我們簡單看看其中的代碼:

public abstract class MethodFilterInterceptor
  extends AbstractInterceptor
{
//該set集合保存了該攔截器不需要攔截的所有方法
  protected Set<String> excludeMethods = Collections.emptySet();
  //該set集合保存了所有該攔截器需要攔截的方法
  protected Set<String> includeMethods = Collections.emptySet();
  //省略getter,setter方法
  
  //用于攔截action的入口
  public String intercept(ActionInvocation invocation)
    throws Exception
  {
    if (applyInterceptor(invocation)) {
      return doIntercept(invocation);
    }
    return invocation.invoke();
  }
  
  //判斷當前需要調用的action處理邏輯方法是否需要被此攔截器攔截
  protected boolean applyInterceptor(ActionInvocation invocation)
  {
    String method = invocation.getProxy().getMethod();
    
    boolean applyMethod = MethodFilterInterceptorUtil.applyMethod(this.excludeMethods, this.includeMethods, method);
    if ((this.log.isDebugEnabled()) && 
      (!applyMethod)) {
      this.log.debug("Skipping Interceptor... Method [" + method + "] found in exclude list.", new String[0]);
    }
    return applyMethod;
  }
  
  //這是需要我們重寫的方法,具體作用下文介紹
  protected abstract String doIntercept(ActionInvocation paramActionInvocation)
    throws Exception;
}

從上述代碼中可以看出,該抽象類實現了Interceptor接口并完成了基本的實現。除此之外,該類提供了兩個集合用于保存該攔截器需要攔截的所有方法和不需要攔截的所有方法,攔截器入口intercept中會首先判斷此次請求action實例中的邏輯處理方法是否需要被該攔截器攔截,如果需要被攔截,那么將會調用doIntercept我們自己實現的攔截器邏輯。否則直接調用invoke方法執行處理邏輯。所以一般來說,我們只需要重寫doIntercept方法完成攔截器的核心處理即可。

當然此處需要注意一點的是,用于判斷當前請求的處理邏輯方法是否需要被該攔截器攔截的方法applyInterceptor是在intercept中進行校驗的,也就是說在執行doIntercept方法之前excludeMethods和includeMethods的值應當是已經初始化完畢了的。所以我們在doIntercept中再次為這兩個屬性賦值是沒用的,因為已經完成了校驗。一般我們在struts.xml中為這兩個屬性賦值,因為該配置文件是先被加載的。下面我們看個實例:

//自定義一個攔截器
public class MyIntercept extends MethodFilterInterceptor {

    protected  String doIntercept(ActionInvocation action)
            throws Exception{
        System.out.println("攔截開始......");
        String result = action.invoke();
        System.out.println("攔截結束......");
        return result;
    }
}
//引用該攔截器并指定不需要攔截的方法
<action name="test" class="MyPackage.TestAction">
    <interceptor-ref name="test">
                <param name="excludeMethods">execute</param>
    </interceptor-ref>
    <result name="success">/index.jsp</result>
</action>

下面我們看運行的結果截圖:

這里寫圖片描述

顯然我們指明了該攔截器不用攔截方法execute,當然結果顯示的也是如我們所愿。如果我們修改上述struts.xml中內容:

<action name="test" class="MyPackage.TestAction">
    <interceptor-ref name="test">
        <param name="includeMethods">execute</param>
    </interceptor-ref>
    <result name="success">/index.jsp</result>
</action>

我們指定該execute方法是需要被攔截器攔截的,下面運行的結果截圖:

這里寫圖片描述

當然如果需要指定多個方法需要被攔截或者不用被攔截,可以使用英文逗號隔開這些方法,例如:

<param name="includeMethods">方法一,方法二,方法三</param>

最后還有一點是:如果一個方法既被放在了includeMethods中也被放在了excludeMethods中,那么框架將會選擇攔截該方法。

五、有關攔截器機制的其他一些細節
?????攔截器的執行順序是按照引用攔截器的順序決定的,例如我們定義兩個攔截器:

<action name="test" class="MyPackage.TestAction">
    <interceptor-ref name="test"/>
    <interceptor-ref name="test2"/>
    <result name="success">/index.jsp</result>
</action>
這里寫圖片描述

也就是說第一個攔截器攔截action之后,會調用invoke方法,如果還有其他攔截器則會調用下一個攔截器,一層層嵌套,最后結束最外層的攔截器。

上述實例中我們使用param參數為攔截器類中的includeMethods屬性賦值,但是如果是一個攔截器棧中我們有該如何為其中某個具體的攔截器屬性賦值呢?

<interceptor-ref name="攔截器棧">
    <param name="攔截器一.屬性名">屬性值</param>
</interceptor-ref>

至此,我們簡單了解了有關struts2中攔截器器的相關知識,如需深刻理解還要在具體項目中體會,總結不到之處,望海涵!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373

推薦閱讀更多精彩內容

  • 攔截器是Struts2框架的核心,它主要完成解析請求參數、將請求參數賦值給Action屬性、執行數據校驗、文件上傳...
    重山楊閱讀 3,974評論 2 13
  • 概述 什么是Struts2的框架Struts2是Struts1的下一代產品,是在 struts1和WebWork的...
    inke閱讀 2,269評論 0 50
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,781評論 18 139
  • 本文包括: 1、Struts 2 概述2、Struts 2 快速入門3、Struts 2 的執行流程4、配置 st...
    廖少少閱讀 2,980評論 3 13
  • (一)Struts、Spring、Hibernate、Mybatis框技術 1.Struts2.0有幾種標簽庫 【...
    獨云閱讀 3,263評論 0 62