1. 程式人生 > >Struts2 攔截器(Interceptor )原理和配置

Struts2 攔截器(Interceptor )原理和配置

一、Struts2攔截器原理:

Struts2攔截器的實現原理相對簡單,當請求struts2的action時,Struts 2會查詢配置檔案,並根據其配置例項化相對的  

攔截器物件,然後串成一個列表,最後一個一個地呼叫列表中的攔截器。

比如:應用要求使用者登陸,且必須為指定使用者名稱才可以檢視系統中某個檢視資源;否則,系統直接轉入登陸頁面。對於上面的

需求,可以在每個Action的執行實際處理邏輯之前,先執行許可權檢查邏輯,但這種做法不利於程式碼複用。因為大部分Action裡

的許可權檢查程式碼都大同小異,故將這些許可權檢查的邏輯放在攔截器中進行將會更加優雅。

PS:

1. Struts2攔截器是在訪問某個Action或Action的某個方法,欄位之前或之後實施攔截,並且Struts2攔截器是可插拔的,

攔截器是AOP的一種實現.

2. 攔截器棧(Interceptor Stack)。Struts2攔截器棧就是將攔截器按一定的順序聯結成一條鏈。在訪問被攔截的方法

或欄位時,Struts2攔截器鏈中的攔截器就會按其之前定義的順序被呼叫。

二、Struts2 攔截器介面實現

Struts2規定使用者自定義攔截器必須實現com.opensymphony.xwork2.interceptor.Interceptor介面。該介面聲明瞭3個方法,

其中,init和destroy方法會在程式開始和結束時各執行一遍,不管使用了該攔截器與否,只要在struts.xml中聲明瞭

該Struts2攔截器就會被執行。

intercept方法就是攔截的主體了,每次攔截器生效時都會執行其中的邏輯。

void init();

void destroy();

String intercept(ActionInvocation invocation) throws Exception;

1:所有攔截器都使用介面Interceptor ,Action去實現這個介面;

Init()方法:在伺服器起動的時候載入一次,並且只加載一次;

Destroy()方法:當攔截器銷燬時執行的方法;

Interceptor()方法:其中裡邊有一個引數invocation;

public String intercept(ActionInvocation invocation) throws xception {

System.out.println("interceptor!!");

String result=invocation.invoke();

return result;

}

其中intercept方法是攔截器的核心方法,所有安裝的攔截器都會呼叫之個方法。在Struts2中已經在struts-default.xml中

預定義了一些自帶的攔截器,如timer、params等。如果在<package>標籤中繼承struts-default,則當前package就會

自動擁有struts-default.xml中的所有配置。

Invocation.invoke()是如果只有一個攔截器執行完這個方法後,會返回給檢視,如果有多個攔截器,它順序的執行完所有的攔截器,

才返回給檢視,也就是呼叫後面的action繼續執行。

二、Struts2 攔截器詳細配置:

預設攔截器是在不設定任何攔截器的時候,給予預設設定的,當只要設定任何一個攔截器就會覆蓋掉預設攔截器,

故此,我們需要手動設定

一旦實現了檢查攔截器,就可以在所有需要實現許可權控制的Action中複用上面的攔截器。

為了使用該攔截器,首先在struts.xml檔案中定義攔截器,定義攔截器的配置片段如下: 

<!-- 使用者攔截器定義在該元素下 --> 

<interceptors> 

<!-- 定義了一個名為authority的攔截器 --> 

<interceptor name="authority" class="lee.AuthorityInterceptor"/> 

</interceptors> 

定義了該攔截器之後,可以在Action中應用該攔截器,應用該攔截器的配置片段如下: 

<!-- 定義一個名為viewBook的Action,其實現類為ActionSupport --> 

<action name="viewBook"> 

<!-- 返回success檢視名時,轉入/WEB-INF/jsp/viewBook.jsp頁面 --> 

<result>/WEB-INF/jsp/viewBook.jsp</result> 

<!-- 攔截器一般配置在result元素之後! --> 

<interceptor-ref name="defaultStack"/> 

<!-- 應用自定義攔截器 --> 

<interceptor-ref name="authority"/> 

</action> 

上面名為viewBook的Action,沒有指定class屬性,預設使用ActionSupport類,配置該Action時,只是指定了一個Result,

指定返回success字串時,系統將轉入/WEBINF/jsp/viewBook.jsp頁面。但併為未配置login檢視對應的JSP頁面。

考慮到這個攔截器的重複使用,可能在多個Action都需要跳轉到login邏輯試圖,故將login Result定義成一個全域性Result。

下面是配置login Result的配置片段: 

<!-- 定義全域性Result -->

<global-results>

<!-- 當返回login檢視名時,轉入/login.jsp頁面 -->

<result name="login">/login.jsp</result>

</global-results>

經過上面的配置,如果瀏覽者在瀏覽器中直接傳送viewBook請求,將會轉入如圖所示的頁面。

這種通過攔截器進行許可權控制的方式,顯然具有更好的程式碼複用。 

如果為了簡化struts.xml檔案的配置,避免在每個Action中重複配置該攔截器,可以將該攔截器配置成一個預設攔截器棧

(這個預設攔截器棧應該包括default-stack攔截器棧和許可權檢查攔截器)。

定義自己的預設攔截器棧的配置片段如下: 

<interceptors>

<!-- 定義許可權檢查攔截器 -->

<interceptor name="authority" class="lee.AuthorityInterceptor"/>

<!-- 定義一個包含許可權檢查的攔截器棧 -->

<interceptor-stack name="mydefault">

<!-- 定義攔截器棧包含default-stack攔截器棧 -->

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

<!-- 定義攔截器棧包含authority攔截器 -->

<interceptor-ref name=" authority"/>

</interceptor- stack >

</interceptors>

一旦定義了上面的mydefault攔截器棧,這個攔截器棧包含了許可權檢查攔截器和系統預設的攔截器棧。如果將這個

攔截器棧定義成預設攔截器,則可以避免在每個Action需要重複定義許可權檢查攔截器。

下面是定義預設攔截器的配置片段:

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

一旦在某個包下定義了上面的預設攔截器棧,在該包下的所有Action都會自動增加許可權檢查功能。對於那些不需要

使用許可權控制的Action,將它們定義在另外的包中——這個包中依然使用系統原來的預設攔截器棧,

將不會有許可權控制功能。

PS:攔截器,攔截器棧和預設的攔截器之間的關係

1:攔截器和攔截器棧是一個級別的,也就是說一個攔截器棧中包括許多攔截器, 一個攔截器棧中還可以包括許多攔截器棧,配置如下方式:

<interceptors>

<!-- 先定義攔截器 -->

<interceptor name="myInterceptor" class="com.struts2.interceptor.MyInterceptor">

<!-- 指定系統初始化給攔截器的引數 -->

<param name="hello">張--</param>

</interceptor>

<!-- 加到自己設定的攔截器棧裡邊去 -->

<interceptor-stack name="myStack">

<interceptor-ref name="myInterceptor">

</interceptor-ref>

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

</interceptor-stack>

</interceptors>

攔截器的使用:

1.先定義;

2.在引用使用;

<interceptor name="myInterceptor" class="com.struts2.interceptor.MyInterceptor">

<interceptor-ref name="myInterceptor">

</interceptor-ref>

2:struts2中有一個系統預設的攔截器棧是 defaultStack,如果你手動引用自己的攔截器,系統預設的攔截器棧將不起作用;

這樣必需手動引入系統的攔截器棧<interceptor-ref name="defaultStack">

</interceptor-ref>

如果想改變系統預設的攔截器棧,可以這樣配置:

<default-interceptor-ref name="myStack">

</default-interceptor-ref>其中myStack是自己定義的攔截器棧名字;

如果攔截器棧中有多個攔截器,在執行action之前的順序跟配置攔截器的順序一致,而在action之後執行的順序是相反的;

PS:最後還附加一點過濾器的東西

過濾器,是在java web中,你傳入的request,response提前過濾掉一些資訊,或者提前設定一些引數,然後再傳入servlet

或者struts的 action進行業務邏輯,比如過濾掉非法url(不是login.do的地址請求,如果使用者沒有登陸都過濾掉),或者在

傳入servlet或者 struts的action前統一設定字符集,或者去除掉一些非法字元

攔截器,是在面向切面程式設計的就是在你的service或者一個方法,前呼叫一個方法,或者在方法後呼叫一個方法比如

動態代理就是攔截器的簡單實現,在你呼叫方法前打印出字串(或者做其它業務邏輯的操作),也可以在你呼叫

方法後打印出字串,甚至在你丟擲異常的時候做業務邏輯的操作。

攔截器與過濾器的區別 :

1、攔截器是基於java的反射機制的,而過濾器是基於函式回撥。

2、攔截器不依賴與servlet容器,過濾器依賴與servlet容器。

3、攔截器只能對action請求起作用,而過濾器則可以對幾乎所有的請求起作用。

4、攔截器可以訪問action上下文、值棧裡的物件,而過濾器不能訪問。

5、在action的生命週期中,攔截器可以多次被呼叫,而過濾器只能在容器初始化時被呼叫一次

6、執行順序 :過濾前 - 攔截前 - Action處理 - 攔截後 - 過濾後。

過濾是一個橫向的過程,首先把客戶端提交的內容進行過濾(例如未登入使用者不能訪問內部頁面的處理);過濾通過後,

攔截器將檢查使用者提交資料的驗證,做一些前期的資料處理,接著把處理後的資料發給對應的Action;

Action處理完成返回後,攔截器還可以做其他過程(還沒想到要做啥),再向上返回到過濾器的後續操作。

一個Filter 可負責攔截多個請求或響應:一個請求或響應也可被多個請求攔截。

建立一個Filter 只需兩個步驟:

(1)建立Filter 處理類:

(2)在web.xml 檔案中配置Filter 。

建立Filter 必須實現javax.servlet.Filter 介面,在該介面中定義了三個方法。

• void init(FilterConfig config): 用於完成Filter 的初始化。

• void destroy(): 用於Filter 銷燬前,完成某些資源的回收。

• void doFilter(ServletRequest request, ServletResponse response,FilterChain chain): 實現過濾功能,

該方法就是對每個請求及響應增加的額外處理。 

過濾器Filter也具有生命週期:init()->doFilter()->destroy(),由部署檔案中的filter元素驅動。在servlet2.4中,

過濾器同樣可以用於請求分派器,但須在web.xml中宣告,<dispatcher>INCLUDE或FORWARD或REQUEST

或ERROR</dispatcher>該元素位於filter-mapping中。