1. 程式人生 > >struts2學習之---攔截器機制

struts2學習之---攔截器機制

攔截器體系是struts2框架的重要組成部分,可以把struts2理解成一個空容器,而大量的內建攔截器完成了該框架的大部分操作。比如:param攔截器負責解析HTTP請求的引數,並設定Action的屬性;servlet-config攔截器直接將HTTP請求中的HttpServletRequest例項和HttpServletResponse例項傳給Action;fileUpload攔截器則負責解析請求引數中的檔案域,並將一個檔案域設定成Action的三個屬性。
struts2攔截器是可插拔式的攔截器。

1.攔截器在struts2中的作用

struts2框架的絕大部分功能都是通過攔截器來完成的。當StrutsPrepareAndExecuteFilter攔截到使用者請求之後,大量攔截器將會對使用者請求進行處理,然後才會呼叫使用者開發的Action例項的方法來處理使用者請求。
攔截器與Action的關係:StrutsPrepareAndExecuteFilter初始化一個AcitonProxy例項,並呼叫它的execute()方法,攔截器方法會先攔截,並處理使用者請求,然後才到Action的execute方法處理使用者請求,返回一個邏輯檢視名,系統負責將邏輯檢視對應的資源呈現給使用者。
struts2已經預設啟用了大量通用功能的攔截器,只要配置Action時所在的package繼承了struts-default包,這些攔截器就會起作用。

2.配置攔截器

在struts.xml檔案中定義攔截器只需為攔截器類指定一個攔截器名。

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

如果需要在配置攔截器時傳入攔截器引數,則需要在<interceptor.../>元素中使用<param.../>子元素。

<package name="">
  <interceptors>
    <interceptor name="攔截器名" class="攔截器實現類">
        <param name="引數名"
>
引數值</param> </interceptor> <interceprors> </package>

把多個攔截器連在一起組成攔截器棧,如果需要在Action執行前同時做登入檢查,安全檢查和記錄日誌,則可以把這三個動作對應的攔截器組成一個攔截器棧。

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

因為攔截器棧與攔截器的功能幾乎完全相同,因此可能出現的是攔截器棧裡也可包含攔截器棧。
系統為攔截器指定引數有如下兩個時機:

  • 定義攔截器時指定引數值:這種引數值將作為攔截器引數的預設引數值。通過<interceptor>元素來定義攔截器。
  • 使用攔截器時指定引數值:在配置Action時攔截器引數指定值。通過<interceptor-ref>元素來使用攔截器。
    使用攔截器時指定的引數值將會覆蓋預設的引數值。

3.使用攔截器的配置語法

一旦定義了攔截器和攔截器棧後,就可以使用這個攔截器或攔截器棧來攔截Action了,攔截器(包含攔截器棧)的攔截行為將會在Action的execute方法執行之前被執行。
通過<interceptor-ref.../>元素可以在Action內使用攔截器。

    <action name="" calss="">
        <interceptor-ref name="攔截器名或攔截器棧名"/>
        <interceptor-ref name="攔截器名或攔截器棧名"/>
    </action>

4.配置預設攔截器

當配置一個包時,可以為其指定預設攔截器。一旦為某個包指定了預設的攔截器,如果該包中的Action沒有顯示指定攔截器,則預設的攔截器將會起作用。一旦為該包中的Action顯示應用了某個攔截器,則預設的攔截器不會起作用,如果該Action還需要使用該預設攔截器,則必須手動配置該攔截器的引用。

    <package name="">
        <default-inerceptor-ref name="已經存在的攔截器或攔截器棧名"/>
    </package>

每個<package...>元素只能有一個<default-interceptor-ref.../>子元素,即每個包只能指定一個預設攔截器。
配置預設攔截器是一種使用攔截器的方式—避免在每個Action中單獨配置攔截器,通過在該包下配置攔截器,可以實現為該包下所有Action同時配置相同的攔截器。

struts-default.xml
<struts>
    <package name="struts-default">
        <default-interceptor-ref name="defaultStack"/>
    </package>
</struts>

攔截器相關配置如下:

  • <interceptors.../>元素:該元素用於定義攔截器。該元素包含<interceptor.../><interceptor-stack.../>子元素用於定義攔截器和攔截器棧。
  • <interceptor.../>:該元素用於定義單個攔截器,指定name和class屬性,分別指定攔截器的名字和實現類。
  • <interceptor-stack.../>:該元素用於定義攔截器棧。包含多個<interceptor-ref.../>元素,用於將多個攔截器或攔截器棧組合成一個新的攔截器棧。
  • <interceptor-ref.../>:該元素引用一個攔截器或攔截器棧,表明應用指定攔截器。name屬性,該屬性值為一個已經定義的攔截器或攔截器棧。該元素可以作為<interceptor-stack.../><action.../>元素的子元素使用。
  • <param.../>:該元素用於為攔截器指定引數,可以作為<interceptor.../><interceptor-ref.../>元素的子元素使用。
  • <default-interceptor-ref.../>:該元素為指定包配置預設攔截器。該元素作為<package.../>元素的子元素使用。

5.實現攔截器類

如果使用者要開發自己的攔截器類,應該實現com.opensymphony.xwork2.interceptor.Interceptor介面。

    public interface Interceptor extends Serializable{
        void destroy();
        void init();
        String intercept(ActionInvocation invocation) throws Exception;
    }
  • init():在該攔截器被例項化之後,在該攔截器執行攔截之前,系統將回調該方法。該方法的方法體主要用於初始化資源。
  • destroy():在攔截器例項被銷燬之前,系統將回調該攔截器的destory方法,該方法用於銷燬在init()方法裡開啟的資源。
  • intercept():該方法是使用者需要實現的攔截動作。該方法的ActionInvocation引數包含了被攔截的Action的引用,可以通過呼叫該引數的invoke方法,將控制權轉給下一個攔截器,或者轉給Action的execute方法。

struts2還提供了一個AbstractInterceptor類,該類提供了一個init()和destory()方法的空實現,如果實現的攔截器不需要開啟資源,則無須實現這兩個方法。

6.攔截方法的攔截器

在某些情況下,如果不想攔截所有的方法,只需要攔截指定方法,此時就需要使用struts2攔截器的發放過濾特性。
為了實現方法過濾特性,struts2提供了一個MethodFilterInterceptor類,該類是AbstractInterceptor類的子類。MethodFilterInterceptor類重寫了AbstractInterceptor類的intercept()方法,但提供了一個doIntercept()抽象方法。
實際上方法過濾的攔截器與實現普通攔截器並沒有太大的區別,只需要注意的是:實現方法過濾的攔截器需要繼承MethodFilterInterceptor抽象類,並且重寫doIntercept()方法定義對Action的攔截邏輯。
在MethodFilterInterceptor方法中,額外增加如下兩個方法:

  • public void setExcludeMethods(String execludeMethods):排除需要過濾的方法—設定方法“黑名單”,所有在excludeMethods字串中列出的方法都不會被攔截。
  • public void setIncludeMethods(String includeMethods):設定需要過濾的方法—設定方法“白名單”,所有在includeMethods字串中列出的方法都會被攔截。
    如果一個方法同時在excludeMethods和includeMethods中列出,則該方法會被攔截。
<action name="" class="">
    <interceptor-ref name="">
        <param name="excludeMethods">execute</param>
    </interceptor>
</action>

struts2中提供了這種方法過濾的攔截器有如下幾個:

  • TokenInterceptor
  • TokenSessionStoreInterceptor
  • DefaultWorkflowInterceptor
  • ValidationInterceptor

7.攔截器的執行順序

在Action的控制方法執行之前,位於攔截器鏈前面的攔截器將先發生作用;在Action的控制方法執行之後,位於攔截器鏈前面的攔截器將後發生作用。

8.攔截結果的監聽器

為了精確定義在execute()方法執行結束後,在處理物理資源轉向之前的動作,struts2提供了用於攔截結果的監聽器,這個監聽器是用過手動註冊在攔截器內部的。
PreResultListener,把該監聽器註冊在攔截器中,這樣可以只要該攔截器起作用的地方,這個攔截介面的監聽器都會被觸發。

    public class MyPreResultListener implements PreResultListener{
        public void beforeResult(ActionInvocation invocation,String resultCode){

        }
    }

public class BeforeResultInterceptor extends AbstractInterceptor{
        public String intercept(ActionInvocation invocation){
            invocation.addPreResultListener(new MyPreResultListener());
        }
    }

resultCode,這個引數就是被攔截的Action的被攔截方法的返回值。
不要在PreResultListener監聽器的beforeResult方法中通過ActionInvocation引數呼叫invoke()方法。

9.覆蓋攔截器棧裡特定攔截器的引數

有時候,Action需要使用一個攔截器棧,當使用這個攔截器棧時,又需要覆蓋該攔截器棧中某個攔截器的指定引數值。此時需要在配置使用攔截器棧的<interceptor-ref.../>元素中使用<param.../>元素來傳入引數,在<param.../>元素中指定引數名時應使用<攔截器名>.<引數名>這種方式。

<package name="">
    <interceptors>
        <interceptor-ref name="first">
            <param name="name">第一個</param>
        </interceptor-ref>
    <interceptors>
    <action name="" class="">
        <interceptor-ref name="defaultStack"/>
        <interceptor-ref name="first">
            <param name="first.name">改名後的攔截器</param>
        </interceptor-ref>
    </action>
</package>