Struts2攔截器的實(shí)現(xiàn)原理相對簡單,當(dāng)請求struts2的action時,Struts 2會查找配置文件,并根據(jù)其配置實(shí)例化相對的 攔截器對象,然后串成一個列表,最后一個一個地調(diào)用列表中的攔截器。
比如:應(yīng)用要求用戶登陸,且必須為指定用戶名才可以查看系統(tǒng)中某個視圖資源;否則,系統(tǒng)直接轉(zhuǎn)入登陸頁面。對于上面的需求,可以在每個Action的執(zhí)行實(shí)際處理邏輯之前,先執(zhí)行權(quán)限檢查邏輯,但這種做法不利于代碼復(fù)用。因為大部分Action里的權(quán)限檢查代碼都大同小異,故將這些權(quán)限檢查的邏輯放在攔截器中進(jìn)行將會更加優(yōu)雅。
PS:
1. Struts2攔截器是在訪問某個Action或Action的某個方法,字段之前或之后實(shí)施攔截,并且Struts2攔截器是可插拔的,攔截器是AOP的一種實(shí)現(xiàn).
2. 攔截器棧(Interceptor Stack)。Struts2攔截器棧就是將攔截器按一定的順序聯(lián)結(jié)成一條鏈。在訪問被攔截的方法或字段時,Struts2攔截器鏈中的攔截器就會按其之前定義的順序被調(diào)用。
二、Struts2 攔截器接口實(shí)現(xiàn):
Struts2規(guī)定用戶自定義攔截器必須實(shí)現(xiàn)com.opensymphony.xwork2.interceptor.Interceptor
接口。該接口聲明了3個方法,其中,init和destroy方法會在程序開始和結(jié)束時各執(zhí)行一遍,不管使用了該攔截器與否,只要在struts.xml中聲明了該Struts2攔截器就會被執(zhí)行。intercept方法就是攔截的主體了,每次攔截器生效時都會執(zhí)行其中的邏輯。
1
2
3
|
void init(); void destroy(); String intercept(ActionInvocation invocation) throws Exception; |
1:所有攔截器都使用接口Interceptor ,Action去實(shí)現(xiàn)這個接口;
-
Init()
方法:在服務(wù)器起動的時候加載一次,并且只加載一次; -
Destroy()
方法:當(dāng)攔截器銷毀時執(zhí)行的方法; -
Interceptor()
方法:其中里邊有一個參數(shù)invocation;
1
2
3
4
5
|
public String intercept(ActionInvocation invocation) throws xception { System.out.println( "interceptor!!" ); String result=invocation.invoke(); return result; } |
其中intercept方法是攔截器的核心方法,所有安裝的攔截器都會調(diào)用之個方法。在Struts2中已經(jīng)在struts-default.xml中預(yù)定義了一些自帶的攔截器,如timer、params等。如果在<package>標(biāo)簽中繼承struts-default,則當(dāng)前package就會自動擁有struts-default.xml中的所有配置。
Invocation.invoke()
是如果只有一個攔截器執(zhí)行完這個方法后,會返回給視圖,如果有多個攔截器,它順序的執(zhí)行完所有的攔截器,才返回給視圖,也就是調(diào)用后面的action繼續(xù)執(zhí)行。
二、Struts2 攔截器詳細(xì)配置:
默認(rèn)攔截器是在不設(shè)置任何攔截器的時候,給予默認(rèn)設(shè)置的,當(dāng)只要設(shè)置任何一個攔截器就會覆蓋掉默認(rèn)攔截器, 故此,我們需要手動設(shè)置
一旦實(shí)現(xiàn)了檢查攔截器,就可以在所有需要實(shí)現(xiàn)權(quán)限控制的Action中復(fù)用上面的攔截器。
為了使用該攔截器,首先在struts.xml文件中定義攔截器,定義攔截器的配置片段如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<!-- 用戶攔截器定義在該元素下 --> <interceptors> <!-- 定義了一個名為authority的攔截器 --> <interceptor name= "authority" class = "lee.AuthorityInterceptor" /> </interceptors> 定義了該攔截器之后,可以在Action中應(yīng)用該攔截器,應(yīng)用該攔截器的配置片段如下: <!-- 定義一個名為viewBook的Action,其實(shí)現(xiàn)類為ActionSupport --> <action name= "viewBook" > <!-- 返回success視圖名時,轉(zhuǎn)入/WEB-INF/jsp/viewBook.jsp頁面 --> <result>/WEB-INF/jsp/viewBook.jsp</result> <!-- 攔截器一般配置在result元素之后! --> <interceptor-ref name= "defaultStack" /> <!-- 應(yīng)用自定義攔截器 --> <interceptor-ref name= "authority" /> </action> |
上面名為viewBook的Action,沒有指定class屬性,默認(rèn)使用ActionSupport類,配置該Action時,只是指定了一個Result,指定返回success字符串時,系統(tǒng)將轉(zhuǎn)入/WEBINF/jsp/viewBook.jsp頁面。但并為未配置login視圖對應(yīng)的JSP頁面。
考慮到這個攔截器的重復(fù)使用,可能在多個Action都需要跳轉(zhuǎn)到login邏輯試圖,故將login Result定義成一個全局Result。
下面是配置login Result的配置片段:
1
2
3
4
5
|
<!-- 定義全局Result --> <global-results> <!-- 當(dāng)返回login視圖名時,轉(zhuǎn)入/login.jsp頁面 --> <result name= "login" >/login.jsp</result> </global-results> |
經(jīng)過上面的配置,如果瀏覽者在瀏覽器中直接發(fā)送viewBook請求,將會轉(zhuǎn)入如圖所示的頁面。
這種通過攔截器進(jìn)行權(quán)限控制的方式,顯然具有更好的代碼復(fù)用。
如果為了簡化struts.xml文件的配置,避免在每個Action中重復(fù)配置該攔截器,可以將該攔截器配置成一個默認(rèn)攔截器棧(這個默認(rèn)攔截器棧應(yīng)該包括default-stack攔截器棧和權(quán)限檢查攔截器)。
定義自己的默認(rèn)攔截器棧的配置片段如下:
1
2
3
4
5
6
7
8
9
10
11
|
<interceptors> <!-- 定義權(quán)限檢查攔截器 --> <interceptor name= "authority" class = "lee.AuthorityInterceptor" /> <!-- 定義一個包含權(quán)限檢查的攔截器棧 --> <interceptor-stack name= "mydefault" > <!-- 定義攔截器棧包含 default -stack攔截器棧 --> <interceptor-ref name= "default-stack" /> <!-- 定義攔截器棧包含authority攔截器 --> <interceptor-ref name= " authority" /> </interceptor- stack > </interceptors> |
一旦定義了上面的mydefault攔截器棧,這個攔截器棧包含了權(quán)限檢查攔截器和系統(tǒng)默認(rèn)的攔截器棧。如果將這個攔截器棧定義成默認(rèn)攔截器,則可以避免在每個Action需要重復(fù)定義權(quán)限檢查攔截器。
下面是定義默認(rèn)攔截器的配置片段:
1
|
< default -interceptor-ref name= "mydefault" /> |
一旦在某個包下定義了上面的默認(rèn)攔截器棧,在該包下的所有Action都會自動增加權(quán)限檢查功能。對于那些不需要使用權(quán)限控制的Action,將它們定義在另外的包中——這個包中依然使用系統(tǒng)原來的默認(rèn)攔截器棧,將不會有權(quán)限控制功能。
PS:攔截器,攔截器棧和默認(rèn)的攔截器之間的關(guān)系
1:攔截器和攔截器棧是一個級別的,也就是說一個攔截器棧中包括許多攔截器, 一個攔截器棧中還可以包括許多攔截器棧,配置如下方式:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<interceptors> <!-- 先定義攔截器 --> <interceptor name= "myInterceptor" class = "com.struts2.interceptor.MyInterceptor" > <!-- 指定系統(tǒng)初始化給攔截器的參數(shù) --> <param name= "hello" >張--</param> </interceptor> <!-- 加到自己設(shè)置的攔截器棧里邊去 --> <interceptor-stack name= "myStack" > <interceptor-ref name= "myInterceptor" > </interceptor-ref> <interceptor-ref name= "defaultStack" ></interceptor-ref> </interceptor-stack> </interceptors> |
攔截器的使用:
1.先定義;
2.在引用使用;
1
2
3
|
<interceptor name= "myInterceptor" class = "com.struts2.interceptor.MyInterceptor" > <interceptor-ref name= "myInterceptor" > </interceptor-ref> |
2:struts2中有一個系統(tǒng)默認(rèn)的攔截器棧是 defaultStack,如果你手動引用自己的攔截器,系統(tǒng)默認(rèn)的攔截器棧將不起作用;這樣必需手動引入系統(tǒng)的攔截器棧
1
2
|
<interceptor-ref name= "defaultStack" > </interceptor-ref> |
如果想改變系統(tǒng)默認(rèn)的攔截器棧,可以這樣配置:
1
2
|
< default -interceptor-ref name= "myStack" > </ default -interceptor-ref> |
其中myStack是自己定義的攔截器棧名字;
如果攔截器棧中有多個攔截器,在執(zhí)行action之前的順序跟配置攔截器的順序一致,而在action之后執(zhí)行的順序是相反的;
PS:最后還附加一點(diǎn)過濾器的東西
過濾器,是在java web中,你傳入的request,response提前過濾掉一些信息,或者提前設(shè)置一些參數(shù),然后再傳入servlet或者struts的 action進(jìn)行業(yè)務(wù)邏輯,比如過濾掉非法url(不是login.do的地址請求,如果用戶沒有登陸都過濾掉),或者在傳入servlet或者 struts的action前統(tǒng)一設(shè)置字符集,或者去除掉一些非法字符
攔截器,是在面向切面編程的就是在你的service或者一個方法,前調(diào)用一個方法,或者在方法后調(diào)用一個方法比如動態(tài)代理就是攔截器的簡單實(shí)現(xiàn),在你調(diào)用方法前打印出字符串(或者做其它業(yè)務(wù)邏輯的操作),也可以在你調(diào)用方法后打印出字符串,甚至在你拋出異常的時候做業(yè)務(wù)邏輯的操作。
攔截器與過濾器的區(qū)別 :
1、攔截器是基于java的反射機(jī)制的,而過濾器是基于函數(shù)回調(diào)。
2、攔截器不依賴與servlet容器,過濾器依賴與servlet容器。
3、攔截器只能對action請求起作用,而過濾器則可以對幾乎所有的請求起作用。
4、攔截器可以訪問action上下文、值棧里的對象,而過濾器不能訪問。
5、在action的生命周期中,攔截器可以多次被調(diào)用,而過濾器只能在容器初始化時被調(diào)用一次
6、執(zhí)行順序 :過濾前 - 攔截前 - Action處理 - 攔截后 - 過濾后。
過濾是一個橫向的過程,首先把客戶端提交的內(nèi)容進(jìn)行過濾(例如未登錄用戶不能訪問內(nèi)部頁面的處理);過濾通過后,攔截器將檢查用戶提交數(shù)據(jù)的驗證,做一些前期的數(shù)據(jù)處理,接著把處理后的數(shù)據(jù)發(fā)給對應(yīng)的Action;Action處理完成返回后,攔截器還可以做其他過程(還沒想到要做啥),再向上返回到過濾器的后續(xù)操作。
一個Filter 可負(fù)責(zé)攔截多個請求或響應(yīng):一個請求或響應(yīng)也可被多個請求攔截。
創(chuàng)建一個Filter 只需兩個步驟:
(1)創(chuàng)建Filter 處理類:
(2)在web.xml 文件中配置Filter 。
創(chuàng)建Filter 必須實(shí)現(xiàn)javax.servlet.Filter
接口,在該接口中定義了三個方法。
• void init(FilterConfig config)
: 用于完成Filter 的初始化。
• void destroy()
: 用于Filter 銷毀前,完成某些資源的回收。
• void doFilter(ServletRequest request, ServletResponse response,FilterChain chain)
: 實(shí)現(xiàn)過濾功能,該方法就是對每個請求及響應(yīng)增加的額外處理。
過濾器Filter也具有生命周期:init()->doFilter()->destroy(),由部署文件中的filter元素驅(qū)動。在servlet2.4中,過濾器同樣可以用于請求分派器,但須在web.xml中聲明,<dispatcher>INCLUDE或FORWARD或REQUEST或ERROR</dispatcher>該元素位于filter-mapping中。
實(shí)例教程
下面寫一個自定義攔截器的例子,判斷用戶是否登錄,就是檢查當(dāng)前用戶的session中的user屬性是否為空,如果為空,就跳到登錄頁面,否則,繼續(xù)執(zhí)行.
1.編寫攔截器,在interceptor包下常見一個java類,名為LoginInterceptor,繼承AbstractInterceptor:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public class LoginInterceptor extends AbstractInterceptor{ @Override public String intercept(ActionInvocation invocation) throws Exception { //得到攔截到的action的名稱,看是否是login,當(dāng)是login的時候,不用進(jìn)行下面的檢測了,直接執(zhí)行下一個攔截器 String actionName=invocation.getProxy().getActionName(); if ( "login" .equals(actionName)){ return invocation.invoke(); } //如果不是login.則判斷是否已登錄,及檢測session中key為user的值是否存在,如果不存在,跳回到登錄頁面 String user=(String)invocation.getInvocationContext().getSession().get( "user" ); if (user== null ){ System.out.println( "未登錄" ); return "login" ; } //進(jìn)行到這里.說明用戶已登錄,則跳轉(zhuǎn)到下一個攔截器 return invocation.invoke(); } } |
2,在struts.xml中配置interceptor,主要特別注意的是,當(dāng)使用了自定義的攔截器后,默認(rèn)攔截器將不起作用,默認(rèn)攔截器實(shí)在struts-default.xml中配置的,當(dāng)引用了自定義攔截器,又想使用struts2提供的默認(rèn)攔截器功能,需要手動配置:這里我將默認(rèn)攔截器和我寫的進(jìn)行登錄權(quán)限驗證的攔截器,寫到一個攔截器棧里,然后調(diào)用這個默認(rèn)攔截器棧:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
< package name= "default" namespace= "/" extends = "struts-default" > <interceptors> <!-- 配置自定義的攔截器--> <interceptor name= "checkLogin" class = "com.wang.interceptor.LoginInterceptor" /> <!--配置一個攔截器棧,里面包含自己定義的攔截器和defaultStack默認(rèn)攔截器--> <interceptor-stack name= "myStack" > <interceptor-ref name= "defaultStack" ></interceptor-ref> <interceptor-ref name= "checkLogin" ></interceptor-ref> </interceptor-stack> </interceptors> <!--引用默認(rèn)的攔截器(棧)--> < default -interceptor-ref name= "myStack" ></ default -interceptor-ref> <!--配置一個全局結(jié)果集--> <global-results> <result name= "login" >/login.jsp</result> </global-results> <action name= "login" class = "com.wang.action.LoginAction" > <result>/succ.jsp</result> <result name= "error" >/login.jsp</result> </action> </ package > |
這里我使用了默認(rèn)攔截器標(biāo)簽,即相當(dāng)于在每個action標(biāo)簽下,使用了 <interceptor-ref name="myStack"></interceptor-ref>.jsp頁面和LoginAction類這里就省略了.
再來介紹一下方法攔截器,方法攔截器比action攔截器控制的更加精細(xì),大體實(shí)現(xiàn)方式和action攔截器相同,不同的是它繼承的是MethodFilterInterceptor類,重寫的是doInterceptor()
方法,在struts.xml的配置上也有些不同,大體是這樣:
1
2
3
4
5
6
|
<interceptor-ref name= "methodInterceptor" > <!--配置被攔截的方法--> <param name= "includeMethods" >methodA,methodsB</param> <!--配置不被攔截的方法--> <param name= "excludeMethods" >methodsC,methodsD</param> </interceptor-ref> |
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對服務(wù)器之家的支持。
原文鏈接:http://blog.csdn.net/kingmax54212008/article/details/51777851