1. 理解攔截器
1.1. 什么是攔截器:
攔截器,在AOP(Aspect-Oriented Programming)中用于在某個(gè)方法或字段被訪(fǎng)問(wèn)之前,進(jìn)行攔截然后在之前或之后加入某些操作。攔截是AOP的一種實(shí)現(xiàn)策略。
在Webwork的中文文檔的解釋為——攔截器是動(dòng)態(tài)攔截Action調(diào)用的對(duì)象。它提供了一種機(jī)制可以使開(kāi)發(fā)者可以定義在一個(gè)action執(zhí)行的前后執(zhí)行的代碼,也可以在一個(gè)action執(zhí)行前阻止其執(zhí)行。同時(shí)也是提供了一種可以提取action中可重用的部分的方式。
談到攔截器,還有一個(gè)詞大家應(yīng)該知道——攔截器鏈(Interceptor Chain,在Struts 2中稱(chēng)為攔截器棧Interceptor Stack)。攔截器鏈就是將攔截器按一定的順序聯(lián)結(jié)成一條鏈。在訪(fǎng)問(wèn)被攔截的方法或字段時(shí),攔截器鏈中的攔截器就會(huì)按其之前定義的順序被調(diào)用。
1.2. 攔截器的實(shí)現(xiàn)原理:
大部分時(shí)候,攔截器方法都是通過(guò)代理的方式來(lái)調(diào)用的。Struts 2的攔截器實(shí)現(xiàn)相對(duì)簡(jiǎn)單。當(dāng)請(qǐng)求到達(dá)Struts 2的ServletDispatcher時(shí),Struts 2會(huì)查找配置文件,并根據(jù)其配置實(shí)例化相對(duì)的攔截器對(duì)象,然后串成一個(gè)列表(list),最后一個(gè)一個(gè)地調(diào)用列表中的攔截器。如下圖:
2. 攔截器的配置
Struts 2已經(jīng)為您提供豐富多樣的,功能齊全的攔截器實(shí)現(xiàn)。大家可以至struts2的jar包內(nèi)的struts-default.xml查看關(guān)于默認(rèn)的攔截器與攔截器鏈的配置。
Struts2(XWork)提供的攔截器的功能說(shuō)明:
攔截器 |
名字 |
說(shuō)明 |
Alias Interceptor |
alias |
在不同請(qǐng)求之間將請(qǐng)求參數(shù)在不同名字件轉(zhuǎn)換,請(qǐng)求內(nèi)容不變 |
Chaining Interceptor |
chain |
讓前一個(gè)Action的屬性可以被后一個(gè)Action訪(fǎng)問(wèn),現(xiàn)在和chain類(lèi)型的result(<result type=”chain”>)結(jié)合使用。 |
Checkbox Interceptor |
checkbox |
添加了checkbox自動(dòng)處理代碼,將沒(méi)有選中的checkbox的內(nèi)容設(shè)定為false,而html默認(rèn)情況下不提交沒(méi)有選中的checkbox。 |
Cookies Interceptor |
cookies |
使用配置的name,value來(lái)是指cookies |
Conversion Error Interceptor |
conversionError |
將錯(cuò)誤從ActionContext中添加到Action的屬性字段中。 |
Create Session Interceptor |
createSession |
自動(dòng)的創(chuàng)建HttpSession,用來(lái)為需要使用到HttpSession的攔截器服務(wù)。 |
Debugging Interceptor |
debugging |
提供不同的調(diào)試用的頁(yè)面來(lái)展現(xiàn)內(nèi)部的數(shù)據(jù)狀況。 |
Execute and Wait Interceptor |
execAndWait |
在后臺(tái)執(zhí)行Action,同時(shí)將用戶(hù)帶到一個(gè)中間的等待頁(yè)面。 |
Exception Interceptor |
exception |
將異常定位到一個(gè)畫(huà)面 |
File Upload Interceptor |
fileUpload |
提供文件上傳功能 |
I18n Interceptor |
i18n |
記錄用戶(hù)選擇的locale |
Logger Interceptor |
logger |
輸出Action的名字 |
Message Store Interceptor |
store |
存儲(chǔ)或者訪(fǎng)問(wèn)實(shí)現(xiàn)ValidationAware接口的Action類(lèi)出現(xiàn)的消息,錯(cuò)誤,字段錯(cuò)誤等。 |
Model Driven Interceptor |
model-driven |
如果一個(gè)類(lèi)實(shí)現(xiàn)了ModelDriven,將getModel得到的結(jié)果放在Value Stack中。 |
Scoped Model Driven |
scoped-model-driven |
如果一個(gè)Action實(shí)現(xiàn)了ScopedModelDriven,則這個(gè)攔截器會(huì)從相應(yīng)的Scope中取出model調(diào)用Action的setModel方法將其放入Action內(nèi)部。 |
Parameters Interceptor |
params |
將請(qǐng)求中的參數(shù)設(shè)置到Action中去。 |
Prepare Interceptor |
prepare |
如果Acton實(shí)現(xiàn)了Preparable,則該攔截器調(diào)用Action類(lèi)的prepare方法。 |
Scope Interceptor |
scope |
將Action狀態(tài)存入session和application的簡(jiǎn)單方法。 |
Servlet Config Interceptor |
servletConfig |
提供訪(fǎng)問(wèn)HttpServletRequest和HttpServletResponse的方法,以Map的方式訪(fǎng)問(wèn)。 |
Static Parameters Interceptor |
staticParams |
從struts.xml文件中將<action>中的<param>中的內(nèi)容設(shè)置到對(duì)應(yīng)的Action中。 |
Roles Interceptor |
roles |
確定用戶(hù)是否具有JAAS指定的Role,否則不予執(zhí)行。 |
Timer Interceptor |
timer |
輸出Action執(zhí)行的時(shí)間 |
Token Interceptor |
token |
通過(guò)Token來(lái)避免雙擊 |
Token Session Interceptor |
tokenSession |
和Token Interceptor一樣,不過(guò)雙擊的時(shí)候把請(qǐng)求的數(shù)據(jù)存儲(chǔ)在Session中 |
Validation Interceptor |
validation |
使用action-validation.xml文件中定義的內(nèi)容校驗(yàn)提交的數(shù)據(jù)。 |
Workflow Interceptor |
workflow |
調(diào)用Action的validate方法,一旦有錯(cuò)誤返回,重新定位到INPUT畫(huà)面 |
Parameter Filter Interceptor |
N/A |
從參數(shù)列表中刪除不必要的參數(shù) |
Profiling Interceptor |
profiling |
通過(guò)參數(shù)激活profile |
在struts.xml文件中定義攔截器,攔截器棧:
<package name="my" extends="struts-default" namespace="/manage"> <interceptors> <!-- 定義攔截器 --> <interceptor name="攔截器名" class="攔截器實(shí)現(xiàn)類(lèi)"/> <!-- 定義攔截器棧 --> <interceptor-stack name="攔截器棧名"> <interceptor-ref name="攔截器一"/> <interceptor-ref name="攔截器二"/> </interceptor-stack> </interceptors> ...... </package> |
3. 使用攔截器
一旦定義了攔截器和攔截器棧后,就可以使用這個(gè)攔截器或攔截器棧來(lái)攔截Action了。攔截器的攔截行為將會(huì)在Action的exceute方法執(zhí)行之前被執(zhí)行。
<action name="userOpt" class="org.qiujy.web.struts2.action.UserAction"> <result name="success">/success.jsp</result> <result name="error">/error.jsp</result> <!-- 使用攔截器,一般配置在result之后, --> <!-- 引用系統(tǒng)默認(rèn)的攔截器 --> <interceptor-ref name="defaultStack"/> <interceptor-ref name="攔截器名或攔截器棧名"/> </action> |
此處需要注意的是,如果為Action指定了一個(gè)攔截器,則系統(tǒng)默認(rèn)的攔截器棧將會(huì)失去作用。為了繼續(xù)使用默認(rèn)攔截器,所以上面配置文件中手動(dòng)引入了默認(rèn)攔截器。
4. 自定義攔截器
作為“框架(framework)”,可擴(kuò)展性是不可或缺的。雖然,Struts 2為我們提供如此豐富的攔截器實(shí)現(xiàn),但是這并不意味我們失去創(chuàng)建自定義攔截器的能力,恰恰相反,在Struts 2自定義攔截器是相當(dāng)容易的一件事。
4.1. 實(shí)現(xiàn)攔截器類(lèi):
所有的Struts 2的攔截器都直接或間接實(shí)現(xiàn)接口com.opensymphony.xwork2.interceptor.Interceptor。該接口提供了三個(gè)方法:
1) void init(); 在該攔截器被初始化之后,在該攔截器執(zhí)行攔截之前,系統(tǒng)回調(diào)該方法。對(duì)于每個(gè)攔截器而言,此方法只執(zhí)行一次。
2) void destroy();該方法跟init()方法對(duì)應(yīng)。在攔截器實(shí)例被銷(xiāo)毀之前,系統(tǒng)將回調(diào)該方法。
3) String intercept(ActionInvocation invocation) throws Exception; 該方法是用戶(hù)需要實(shí)現(xiàn)的攔截動(dòng)作。該方法會(huì)返回一個(gè)字符串作為邏輯視圖。
除此之外,繼承類(lèi)com.opensymphony.xwork2.interceptor.AbstractInterceptor是更簡(jiǎn)單的一種實(shí)現(xiàn)攔截器類(lèi)的方式,因?yàn)榇祟?lèi)提供了init()和destroy()方法的空實(shí)現(xiàn),這樣我們只需要實(shí)現(xiàn)intercept方法。
4.2. 使用自定義攔截器:
兩個(gè)步驟:
l 通過(guò)<interceptor …>元素來(lái)定義攔截器。
l 通過(guò)<interceptor-ref …>元素來(lái)使用攔截器。
5. 自定義攔截器示例
5.1. 問(wèn)題描述:
使用自定義攔截器來(lái)完成用戶(hù)權(quán)限的控制:當(dāng)瀏覽者需要請(qǐng)求執(zhí)行某個(gè)操作時(shí),應(yīng)用需要先檢查瀏覽者是否登錄,以及是否有足夠的權(quán)限來(lái)執(zhí)行該操作。
5.2. 實(shí)現(xiàn)權(quán)限控制攔截器類(lèi):
AuthorizationInterceptor.java
package org.qiujy.common; import java.util.Map; import com.opensymphony.xwork2.Action; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; /** *權(quán)限檢查攔截器 * *@authorqiujy *@version1.0 */ publicclass AuthorizationInterceptor extends AbstractInterceptor { /* * 攔截Action處理的攔截方法 * */ public String intercept(ActionInvocation invocation) throws Exception {
Map session = invocation.getInvocationContext().getSession(); String userName = (String) session.get("userName");
if (null != userName && userName.equals("test")) { System.out.println("攔截器:合法用戶(hù)登錄---"); return invocation.invoke(); } else { System.out.println("攔截器:用戶(hù)未登錄---"); return Action.LOGIN; } } } |
5.3. 配置權(quán)限控制攔截器:
struts.xml:
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="my" extends="struts-default">
<interceptors> <!-- 定義權(quán)限控制攔截器 --> <interceptor name="authority" class="org.qiujy.common.AuthorizationInterceptor"/> </interceptors>
<!-- 定義全局處理結(jié)果 --> <global-results> <!-- 邏輯名為login的結(jié)果,映射到/login.jsp頁(yè)面 --> <result name="login">/login.jsp</result> </global-results>
<action name="listall" class="org.qiujy.web.struts2.action.UserAction" method="listAllUser"> <result name="success">/listall.jsp</result> <!-- 使用攔截器 --> <interceptor-ref name="defaultStack"/> <interceptor-ref name="authority"/> </action>
<action name="userOpt" class="org.qiujy.web.struts2.action.UserAction"> <result name="success">/success.jsp</result> </action> </package> </struts> |
其它頁(yè)面見(jiàn)源代碼。
5.4. 運(yùn)行調(diào)試:
在瀏覽器地址欄直接輸入http://localhost:8080/AuthorityInterceptorDemo/listall.action來(lái)訪(fǎng)問(wèn),此動(dòng)作配置了權(quán)限攔截器,所有被轉(zhuǎn)到登錄頁(yè)面。
登錄后:
再訪(fǎng)問(wèn)http://localhost:8080/AuthorityInterceptorDemo/listall.action這個(gè)鏈接:
如果為了簡(jiǎn)化struts.xml文件的配置,避免在每個(gè)Action重復(fù)配置該攔截器,可以將攔截器配置成了一個(gè)默認(rèn)攔截器棧。如下:
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="my" extends="struts-default"> <interceptors> <!-- 定義權(quán)限控制攔截器 --> <interceptor name="authority" class="org.qiujy.common.AuthorizationInterceptor" /> <!-- 定義一個(gè)包含權(quán)限控制的攔截器棧 --> <interceptor-stack name="mydefault"> <interceptor-ref name="defaultStack" /> <interceptor-ref name="authority" /> </interceptor-stack> </interceptors>
<!-- 定義默認(rèn)攔截器 --> <default-interceptor-ref name="mydefault" /> <!-- 定義全局處理結(jié)果 --> <global-results> <!-- 邏輯名為login的結(jié)果,映射到/login.jsp頁(yè)面 --> <result name="login">/login.jsp</result> </global-results> <action name="listall" class="org.qiujy.web.struts2.action.UserAction" method="listAllUser"> <result name="success">/listall.jsp</result> </action> </package>
<package name="font" extends="struts-default"> <action name="userOpt" class="org.qiujy.web.struts2.action.UserAction"> <result name="success">/success.jsp</result> </action> </package> </struts> |
一旦在某個(gè)包下定義了默認(rèn)攔截器棧,在該包下的所有action都會(huì)使用此攔截器棧。對(duì)于那些不想使用些攔截器棧的action,則應(yīng)該將它放置在其它的包下。