攔截器是Struts2框架的核心,它主要完成解析請(qǐng)求參數(shù)、將請(qǐng)求參數(shù)賦值給Action屬性、執(zhí)行數(shù)據(jù)校驗(yàn)、文件上傳等工作。Struts2設(shè)計(jì)的靈巧性,攔截器起了關(guān)鍵性的作用,當(dāng)需要擴(kuò)展Struts2功能時(shí),只需要提供對(duì)應(yīng)攔截器,并將它配置在Struts2容器中即可;如果不需要該功能時(shí),也只需要取消該攔截器的配置即可。
Struts2內(nèi)建了大量的攔截器,這些攔截器以name-class對(duì)的形式配置在struts-default. xml文件中,其中name是攔截器的名字,就是以后我們使用該攔截器的唯一標(biāo)識(shí);class則指定了該攔截器的實(shí)現(xiàn)類,如果我們定義的package繼承了Struts2的默認(rèn)struts-default包,則可以自由使用它下面定義的攔截器,否則必須自己定義這些攔截器。
2.自定義攔截器的實(shí)現(xiàn)
為了實(shí)現(xiàn)某些操作,我們可以自定義攔截器,自定義攔截器有三種方式定義。分別為實(shí)現(xiàn)Interceptor接口,繼承抽象類AbstractInterceptor,繼承MethodFilterInteceptor類。
方式一,實(shí)現(xiàn)Interceptor接口。重寫(xiě)String intercept(ActionInvocation invocation)方法。
package test002Iterceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
/**
* Created by yangcs on 2017/2/1.
* 通過(guò)實(shí)現(xiàn)Interceptor接口,重寫(xiě)String intercept(ActionInvocation invocation)方法自定義一個(gè)攔截器
*/
public class Iterceptor001 implements Interceptor{
@Override
public void destroy() {
}
@Override
public void init() {
}
@Override
public String intercept(ActionInvocation actionInvocation) throws Exception {
System.out.println("開(kāi)始攔截");
String result = actionInvocation.invoke(); //invoke()方法會(huì)把請(qǐng)求傳遞到下一個(gè)攔截器或者最終的action中
System.out.println("結(jié)束攔截");
return result;
}
}
為了使用此攔截器,我們必須將此攔截器進(jìn)行注冊(cè),隨后再在要使用此攔截器的Action中引用。即首先在<package>中注冊(cè),內(nèi)容如下:
<interceptors>
<interceptor name="login001" class="test002Iterceptor.Iterceptor001"></interceptor>
</interceptors>
注冊(cè)完成后,如果我們要在login.action中使用此攔截器,只需要在<action>中增加如下內(nèi)容:
<interceptor-ref name="login001"></interceptor-ref>
實(shí)例流程分析:當(dāng)我們?yōu)長(zhǎng)oginAction配置了攔截器時(shí),并且有客戶端請(qǐng)求此Action時(shí),會(huì)首先被此攔截器攔住,然后執(zhí)行System.out.println("開(kāi)始攔截"),隨后我們調(diào)用invocation.invoke()方法,它會(huì)把請(qǐng)求繼續(xù)傳遞給下一個(gè)攔截器,下一個(gè)攔截器也會(huì)繼續(xù)執(zhí)行相應(yīng)代碼后再調(diào)用invoke方法繼續(xù)傳遞,直到請(qǐng)求到達(dá)最后一個(gè)攔截器,它會(huì)把請(qǐng)求傳遞給Action,比如,我們這里只用到了一個(gè)攔截器,當(dāng)它執(zhí)行完成后,會(huì)把請(qǐng)求直接轉(zhuǎn)交到LoginAction處理,LoginAction處理完成后,它會(huì)返回結(jié)果給MyInterceptor攔截器。
方式二、繼承AbstractInterceptor抽象類,重寫(xiě)String intercept(ActionInvocation invocation)方法
package test002Iterceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import test002.PreResultListenerAction;
/**
* Created by yangcs on 2017/2/1.
* 繼承AbstractInterceptor抽象類,重寫(xiě)String intercept(ActionInvocation invocation)方法實(shí)現(xiàn)一個(gè)攔截器
*/
public class AbsIterceptor002 extends AbstractInterceptor{
@Override
public String intercept(ActionInvocation actionInvocation) throws Exception {
actionInvocation.addPreResultListener(new PreResultListenerAction(){}); //這里調(diào)用了定義好的result監(jiān)聽(tīng)器,則配置了此攔截器的action在發(fā)送result前,會(huì)調(diào)用監(jiān)聽(tīng)器中的beforeResult()方法!
System.out.println("AbstractIntorceptor開(kāi)始攔截");
String result = actionInvocation.invoke();
System.out.println("AbstractIntorceptor結(jié)束攔截");
return result;
}
}
然后注冊(cè)此攔截器,在<interceptors>元素進(jìn)行進(jìn)行配置,內(nèi)容如下:
<interceptor name="login002" class="test002Iterceptor.AbsIterceptor002"></interceptor>
隨后再在LoginAction中引用此攔截器,即在<action name="login" ...>配置如下內(nèi)容:
<interceptor-ref name="login002"></interceptor-ref>
方式三、繼承MethodFilterInteceptor類,重寫(xiě)String doIntercept(ActionInvocation invocation) 方法。
package test002Iterceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
/**
* Created by yangcs on 2017/2/1.
* 繼承MethodFilterInteceptor類,重寫(xiě)String doIntercept(ActionInvocation invocation) 方法實(shí)現(xiàn)一個(gè)攔截器
*/
public class MethodFilterInterceptor003 extends MethodFilterInterceptor{
@Override
protected String doIntercept(ActionInvocation actionInvocation) throws Exception {
System.out.println("MethodFilterInterceptor開(kāi)始攔截");
String result = actionInvocation.invoke();
System.out.println("MethodFilterInterceptor結(jié)束攔截");
return result;
}
}
然后注冊(cè)此攔截器,在<interceptors>元素進(jìn)行進(jìn)行配置,內(nèi)容如下:
<interceptor name="login003" class="test002Iterceptor.AbsIterceptor002"></interceptor>
隨后再在LoginAction中引用此攔截器,即在<action name="login" ...>配置如下內(nèi)容:
<interceptor-ref name="login003"></interceptor-ref>
分析:當(dāng)配置到此,實(shí)質(zhì)便為L(zhǎng)oginAction配置了三個(gè)攔截器,當(dāng)我們點(diǎn)擊登錄時(shí)會(huì)在控制臺(tái)打印出如下語(yǔ)句:
開(kāi)始攔截
Abs開(kāi)始攔截
method開(kāi)始攔截
--先執(zhí)行攔截器,再執(zhí)行此Action
method結(jié)束攔截
Abs結(jié)束攔截
結(jié)束攔截
攔截器的執(zhí)行順序和過(guò)濾器filter一樣,取決于在action標(biāo)簽中的配置順序
其實(shí)當(dāng)我們點(diǎn)擊登錄時(shí),本來(lái)是要訪問(wèn)LoginAction,最后會(huì)把LoginAction的執(zhí)行結(jié)果傳遞給訪問(wèn)者。但是當(dāng)我們配置了攔截器時(shí),當(dāng)我們?nèi)ピL問(wèn)Action時(shí),會(huì)首先被攔截,隨后攔截器執(zhí)行一些操作后才會(huì)繼續(xù)把請(qǐng)求傳遞下去。
下圖說(shuō)明攔截流程:

注:自定義攔截器需要特別注意的是不要忘記引入struts2默認(rèn)的攔截器,可以使用攔截器棧(Interceptor Stack)來(lái)組合多個(gè)攔截器:
<interceptors>
<interceptor name="login001" class="test002Iterceptor.Iterceptor001"></interceptor>
<interceptor name="login002" class="test002Iterceptor.AbsIterceptor002"></interceptor>
<interceptor name="login003" class="test002Iterceptor.MethodFilterInterceptor003"></interceptor>
<interceptor name="interceptor04" class="test002Iterceptor.MethodFilterInterceptor003"></interceptor>
<interceptor-stack name="login">
<interceptor-ref name="login001" ></interceptor-ref>
<interceptor-ref name="login002" ></interceptor-ref>
<interceptor-ref name="login003" ></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
<!--這個(gè)是struts2默認(rèn)的攔截器棧,當(dāng)自定義攔截器時(shí),這個(gè)默認(rèn)的攔截器就會(huì)失效,必須手動(dòng)添加配置,否則就會(huì)有問(wèn)題-->
</interceptor-stack>
</interceptors>
上面分別使用了三種方式來(lái)創(chuàng)建自定義的攔截器,第一種方式是最原始的實(shí)現(xiàn)方式(實(shí)現(xiàn)Interceptor接口),第二種方式的好處是我們可以不必重寫(xiě)所有的方法(繼承AbstractInterceptor抽象類),較常用。第三種方式進(jìn)行了擴(kuò)展(繼承MethodFilterInterceptor類),可以更靈活地對(duì)action中不同的方法進(jìn)行單獨(dú)的攔截:
使用來(lái)MethodFilterInterceptor靈活攔截
步驟一、建立MethodAction,代碼如下:
package com.asm;
import com.opensymphony.xwork2.ActionSupport;
public class MethodAction extends ActionSupport{
public String m1(){
return SUCCESS;
}
public String m2(){
return SUCCESS;
}
public String m3(){
return SUCCESS;
}
}
步驟二、注冊(cè)此Action,并為此Action配置攔截器。配置內(nèi)容如下:
<action name="*_*" class="com.asm.MethodAction" method="{2}">
<result name="success">/{2}Suc.jsp</result>
<interceptor-ref name="myMet">
</interceptor-ref>
</action>
我們?yōu)榇薃ction配置了前面寫(xiě)的MyMethodFilterInterceptor攔截器,并在link.jsp中增加如下鏈接:
<a href="<%=request.getContextPath()%>/Method_m1.action">m1</a><br>
<a href="<%=request.getContextPath()%>/Method_m2.action">m2</a><br>
<a href="<%=request.getContextPath()%>/Method_m3.action">m3</a><br>
當(dāng)點(diǎn)m1時(shí)會(huì)訪問(wèn)到m1Suc.jsp頁(yè)面, 點(diǎn)m2、m3會(huì)分別訪問(wèn)到m2Suc.jsp、m3Suc.jsp頁(yè)面?,F(xiàn)在假如我們想訪問(wèn)m2、m3時(shí)不被攔截,我們只需修改MyMethodFilterInterceptor注冊(cè):修改內(nèi)容為:
<interceptor name="myMet" class="com.asm.MyMethodFilterInterceptor">
<param name="excludeMethods">m2,m3</param>
</interceptor>
它的作用和增加<param name="includeMethods">m1</param>等價(jià)。上面是指定m2,m3方法調(diào)用時(shí)不被攔截,這里是指定只攔截m1。除了這種在注冊(cè)攔截器時(shí)指定攔截外,還可以在引用攔截器時(shí)指定,即如下形式:
<interceptor-ref name="myMet">
<param name="excludeMethods">m2,m3</param>
<param name="includeMethods">m1</param>
</interceptor-ref>
上面的兩處<param>配置是等價(jià)的,但是如果〈param〉配置沖突,誰(shuí)起作用?即如果我們對(duì)m1配置了excludeMethods同時(shí)又配置了includeMethods時(shí),誰(shuí)起作用,我們可以進(jìn)行這些沖突的驗(yàn)證。以下是驗(yàn)證結(jié)果:
引用配置(在Action引用攔截器時(shí)配置)時(shí),以includeMethods的配置為準(zhǔn)。
一旦我們?yōu)閿r截器使用了<param>配置,而對(duì)m1這樣的方法不配置任何,就不會(huì)被攔截。
但是如果不使用<param>,它們?nèi)慷家粩r截。
注冊(cè)配置時(shí)(在注冊(cè)攔截器時(shí)配置),情況和“引用配置”完全一樣。
引用配置和注冊(cè)配置沖突時(shí),以引用配置為準(zhǔn)。
使用默認(rèn)的execAndWait攔截器實(shí)現(xiàn)查詢等待效果
當(dāng)我們進(jìn)行數(shù)據(jù)庫(kù)查詢等相關(guān)的操作時(shí),如果服務(wù)器負(fù)荷過(guò)重可能不能及時(shí)把數(shù)據(jù)查詢出來(lái),進(jìn)而會(huì)在狀態(tài)攔顯示“正在打開(kāi)...”,但卻一直轉(zhuǎn)不到相關(guān)的頁(yè)面,這將給客戶端帶來(lái)不便,甚于很多人會(huì)因此不愿使用網(wǎng)站的所有服務(wù)。對(duì)此我們可以在客戶提交時(shí),馬上轉(zhuǎn)到一個(gè)頁(yè)面,并在該頁(yè)面顯示“您的請(qǐng)求已提交,服務(wù)器正在查詢,請(qǐng)等待...”的內(nèi)容,這樣客戶將不會(huì)陷于無(wú)賴的等待中。
對(duì)于此要求,struts2可以輕松幫我們完成。
下面新建struts2wait項(xiàng)目演示此實(shí)例。
建立LoginAction,代碼如下:
package com.asm;
public class LoginAction extends ActionSupport {
public String execute() throws Exception {
Thread.sleep(5000);
return SUCCESS;
}
}
說(shuō)明:為了模擬服務(wù)器負(fù)荷過(guò)重,查詢時(shí)間要很長(zhǎng)。我們?cè)谑褂昧司€程休眠的方式。
隨后配置此Action,配置的主要內(nèi)容如下:
<action name="login" class="com.asm.LoginAction">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="execAndWait"></interceptor-ref>
<result name="wait">/wait.jsp</result>
<result name="success">/success.jsp</result>
</action>
注意:在配置前我們先是使用了默認(rèn)的攔截器,再此強(qiáng)調(diào)在我們?yōu)锳ction配置攔截器時(shí),應(yīng)該總是配上默認(rèn)的攔截器。隨后我們使用了execAndWait攔截器,如需要配置此攔截器,此攔截器一定要配置在最后,否則會(huì)出現(xiàn)一些難預(yù)知的結(jié)果。如果使用此攔截器,我們通常還會(huì)配置wait的result結(jié)果集,因?yàn)楫?dāng)我們請(qǐng)求的Action在未執(zhí)行完,就是未返回結(jié)果時(shí),會(huì)首先把wait result返回,而在wait result所指定的頁(yè)面中通常會(huì)再次發(fā)送請(qǐng)求給原始的Action。所以wait.jsp的主要內(nèi)容如下:
<head>
<meta http-equiv="refresh" content="1;login.action">
</head>
<body> 查詢請(qǐng)求已提交,正在查詢數(shù)據(jù),請(qǐng)等待... </body>
在此頁(yè)面中,我們指定了每隔1秒便發(fā)送請(qǐng)求到login.action中去。這樣,客戶端便可以及時(shí)獲取查詢結(jié)果。結(jié)合此實(shí)例,我們簡(jiǎn)要分析流程:當(dāng)我們發(fā)出請(qǐng)求到此Login.Action中去時(shí),首先會(huì)被exeAndWait攔截器攔截到,這樣它便跳轉(zhuǎn)到wait.jsp頁(yè)面,在wait.jsp頁(yè)面中每隔1秒我們會(huì)繼續(xù)發(fā)送此Action的請(qǐng)求,當(dāng)再次請(qǐng)求到達(dá)LoginAction時(shí),如果它已經(jīng)返回,則會(huì)跳到此Action返回的頁(yè)面,如果LoginAction未返回,則繼續(xù)停留在wait.jsp中,再隔1秒又再次發(fā)送請(qǐng)求到LoginAction中去。
其實(shí)如果服務(wù)器能很快查詢出結(jié)果,我們則不需要用到wait.jsp頁(yè)面,我們只需在<interceptor-ref name="execAndWait"></interceptor-ref>中增加如下一段配置:
<param name="delay">6000</param>
這樣便延遲請(qǐng)求到達(dá)wait.jsp頁(yè)面,這樣當(dāng)請(qǐng)求到達(dá)時(shí)它會(huì)在LoginAction中執(zhí)行6秒時(shí)間再到wait.jsp,而6秒LoginAction足以執(zhí)行完并返回結(jié)果,所以當(dāng)攔截器
執(zhí)行時(shí)首先檢查到此Action已經(jīng)返回結(jié)果。則攔截器會(huì)直接用此返回頁(yè)面,如果此時(shí)發(fā)現(xiàn)LoginAction并未執(zhí)行完,它便會(huì)把wait resutl指定的頁(yè)面返回。
使用默認(rèn)的TokenInterceptor攔截器防止表單重復(fù)提交
由于某些原因,用戶在進(jìn)行類似表單提交的操作后,以為表單未被提交,會(huì)進(jìn)行多次的重復(fù)提交。為了避免用戶多次提交給服務(wù)器帶來(lái)負(fù)荷。我們會(huì)對(duì)表單提交這樣的操作進(jìn)行一些處理,以告訴用戶不要重復(fù)提交。
下面我們建立struts2token項(xiàng)目,使用struts2的token攔截器來(lái)實(shí)現(xiàn)此案例(也可以通過(guò)自己在session中生成token令牌來(lái)實(shí)現(xiàn)這一效果:使用Session防止表單重復(fù)提交)。
步驟一,編寫(xiě)login.jsp頁(yè)面,內(nèi)容如下:
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<html>
<body>
<form action="<%=request.getContextPath()%>/login.action" >
姓名:<input type="text" name="username"><br>
密碼:<input type="password" name="password"><br>
<input type="submit" value="登錄">
<s:token></s:token>
</form>
</body>
</html>
說(shuō)明,此登錄頁(yè)面中的關(guān)鍵技術(shù)就是使用了標(biāo)簽庫(kù)中的<s:token></s:token>標(biāo)簽,它的作用就是在用戶訪問(wèn)此頁(yè)面時(shí)會(huì)生成一個(gè)sessionId,在提交時(shí)會(huì)服務(wù)器會(huì)據(jù)此驗(yàn)證表單是否已提交?!癟o set a token in your form, you should use the token tag. This tag is required and must be used in the forms that submit to actions protected by this interceptor”,這句話的大概意思就是我們必須要在提交的表單中使用這個(gè)token tag,這樣提交到的Action便能配置TokenInterceptor攔截器驗(yàn)證表單是否重復(fù)提交。
步驟二,編寫(xiě)LoginAction,主要代碼如下:
package com.asm;
public class LoginAction extends ActionSupport {
public String execute() throws Exception {
System.out.println("---->執(zhí)行execute方法...");
return SUCCESS;
}
}
步驟三,struts.xml主要配置內(nèi)容如下:
<struts>
<package name="tokenTest" extends="struts-default">
<action name="login" class="com.asm.LoginAction">
<result name="success">/success.jsp</result>
<result name="invalid.token">/subError.jsp</result>
<interceptor-ref name="token"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</action>
</package>
</struts>
說(shuō)明:在此Action下,我們配置了token攔截器,另注意到在此Action下我們還配置了一個(gè)“invalid.token”result,因?yàn)樘峤粫r(shí)服務(wù)器如果根據(jù)token標(biāo)簽產(chǎn)生的sessionId判斷出表單已提交,它則返回invalid.token指向的視圖。比如這里,如果重復(fù)提交則會(huì)轉(zhuǎn)到.../subError.jsp中去。另不要忘記了引入默認(rèn)的攔截器棧。補(bǔ)充:關(guān)于token攔截器更多細(xì)節(jié)可以訪問(wèn)org.apache.struts2.interceptor.TokenInterceptor類的api說(shuō)明。
步驟四,編寫(xiě)配置中所用到j(luò)sp頁(yè)面,這些頁(yè)面編寫(xiě)簡(jiǎn)單,在此省去。
步驟五、發(fā)布測(cè)試,請(qǐng)注意訪問(wèn)login.jsp頁(yè)面時(shí),查看源文件時(shí)會(huì)發(fā)現(xiàn)增加了兩個(gè)隱藏域信息。
步驟六、更換攔截器:我們還可以使用tokenSession攔截器,它的功能比上面的增強(qiáng),它能保證持有相同sessionId的并發(fā)請(qǐng)求等待第一個(gè)完成之后才能被提交處理,但是它返回的是action執(zhí)行后的result.接著上例,我們只需要在配置中作如下修改:把上面的token攔截器改成
<interceptor-ref name="tokenSession"></interceptor-ref>
即可。隨后便可以測(cè)試,測(cè)試時(shí)會(huì)發(fā)現(xiàn)如果我們重復(fù)提交,它總是返回到上一次的success.jsp頁(yè)面,但是它并不是經(jīng)過(guò)LoginAction中的execute處理后返回(我們System.out.print語(yǔ)句在重復(fù)提交時(shí)并未打印出來(lái)),而是此攔截器判斷出是重復(fù)后直接返回上一次提交轉(zhuǎn)向的頁(yè)面。
使用攔截器實(shí)現(xiàn)權(quán)限驗(yàn)證
為了說(shuō)明此問(wèn)題,我們建立struts2auth項(xiàng)目,流程圖如下:

簡(jiǎn)短說(shuō)明:當(dāng)我們?cè)L問(wèn)main.jsp頁(yè)面,并試圖通過(guò)此頁(yè)面中的鏈接地址:note.action來(lái)訪問(wèn)到.../WEB-INF/note.jsp頁(yè)面時(shí),由于訪問(wèn)的note.action配置了攔截器,所以會(huì)被攔截,如果攔截器判斷登錄則可以訪問(wèn),否則會(huì)跳到登錄頁(yè)面。如果我們從登錄頁(yè)面直接到main.jsp頁(yè)面,再來(lái)訪問(wèn)note.action時(shí),同樣被攔截但是由于登錄過(guò),所以可以訪問(wèn)到此action對(duì)應(yīng)的內(nèi)容。由這里的分析可以看出關(guān)鍵點(diǎn)就登錄成功時(shí)給出標(biāo)志提供給攔截器判斷是否成功登錄。
步驟一,搭建好相關(guān)的開(kāi)發(fā)環(huán)境,并準(zhǔn)備好登錄頁(yè)面login.jsp,代碼如下:
<form action="<%=request.getContextPath()%>/login.action" method="post">
姓名:<input type="text" name="username"><br>
密碼:<input type="password" name="password"><br>
<input type="submit" value="登錄">
</form>
步驟二,建立相應(yīng)的Action:LoginAction。代碼如下:
package com.asm;
public class LoginAction extends ActionSupport {
private String username;
Map session;
public String execute() throws Exception {
if(username.equals("admin")){
session = ActionContext.getContext().getSession();
session.put("loginSign", "loginSuccess");
return SUCCESS;
}else{
return LOGIN;
}
}
...省略u(píng)sername的get/set方法
}
說(shuō)明:我們這里是設(shè)定了只有登錄用戶名為admin時(shí),此Action才設(shè)置登錄標(biāo)志。另這里獲取Session對(duì)象采取的是“與Servlet解耦合的非IOC方式”。
步驟三,編寫(xiě)攔截器類,代碼如下:
package com.asm.interceptor;
public class AuthInterceptor extends AbstractInterceptor {
public String intercept(ActionInvocation invocation) throws Exception {
Map session = invocation.getInvocationContext().getSession();
// session=ActionContext.getContext().getSession();
if (session.get("loginSign") == null) {
return "login";
} else {
String result = invocation.invoke();
return result;
}
}
}
步驟四,配置此Action相關(guān),主要配置內(nèi)容如下:
<struts>
<package name="tokenTest" extends="struts-default">
<interceptors>
<interceptor name="auth"
class="com.asm.interceptor.AuthInterceptor">
</interceptor>
<interceptor-stack name="authStack">
<interceptor-ref name="auth"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<action name="login" class="com.asm.LoginAction">
<result name="success">/main.jsp</result>
<result name="login">/login.jsp</result>
</action>
<action name="note">
<result>/WEB-INF/note.jsp</result>
<result name="login">/login.jsp</result>
<interceptor-ref name="authStack"></interceptor-ref>
</action>
</package>
</struts>
說(shuō)明:結(jié)合前面的一些代碼來(lái)看,當(dāng)我們?yōu)閚ote.action配置了前面寫(xiě)所的AuthInterceptor攔截器時(shí),如果我們要訪問(wèn)note.action,攔截器會(huì)首先判斷是否登錄,如果登錄則繼續(xù)把請(qǐng)求傳遞下去,如果沒(méi)有登錄則會(huì)返回到登錄頁(yè)面。
使用默認(rèn)的AnnotationWorkflowInterceptor攔截器為action方法添加注解調(diào)用
AnnotationWorkflowInterceptor此攔截器可以調(diào)用在Action中任何有注解的方法。下面我們來(lái)演示它的使用,具體步驟如下:
步驟一,建立struts2annotationInt項(xiàng)目,并建立LoginAction類,代碼如下:
package com.asm;
...省略導(dǎo)入的包
public class LoginAction extends ActionSupport {
private String username;
@Before
public String myBefore() {
System.out.println("調(diào)用myBefore方法");
return LOGIN;
}
@After
public void myAfter() throws InterruptedException {
Thread.sleep(5000);
System.out.println("----調(diào)用myAfter方法");
}
@BeforeResult
public void myBeforeResult() {
System.out.println("----調(diào)用myBeforeResult方法");
}
public String execute() throws Exception {
System.out.println("調(diào)用execute方法");
return SUCCESS;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
System.out.println("---調(diào)用set方法" + username);
this.username = username;
}
}
說(shuō)明:要想使用方法成為被攔截器監(jiān)視的注解方法,只需在方法關(guān)加上@...這樣的形式并導(dǎo)入相關(guān)的類即可。
步驟二,編寫(xiě)相關(guān)的jsp及配置該Action,主要配置內(nèi)容如下:
<struts>
<package name="ano" extends="struts-default">
<interceptors>
<interceptor name="anno" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor">
</interceptor>
<interceptor-stack name="annoStack">
<interceptor-ref name="anno"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<action name="login" class="com.asm.LoginAction">
<result name="success">/success.jsp</result>
<result name="login">/login.jsp</result>
<interceptor-ref name="annoStack"></interceptor-ref>
</action>
</package>
</struts>
結(jié)合配置說(shuō)明:當(dāng)我們?yōu)長(zhǎng)oginAction配置了AnnotationWorkflowInterceptor攔截器時(shí),LoginAction中的所有注解方法才真正生效。下面重點(diǎn)是來(lái)討論這些方法的執(zhí)行順序及作用。
加@Before注解的方法意思是在action的execute方法執(zhí)行之前被調(diào)用,但是此方法如果返回不為空的話,它的返回結(jié)果將是真正的返回結(jié)果,比如這里我們r(jià)eturn LOGIN,這樣無(wú)論以什么用戶名登錄,它總會(huì)返回到login result(這里為login.jsp頁(yè)面) 。但是從執(zhí)前結(jié)果來(lái)看,在返回前仍執(zhí)行了標(biāo)記為@BeforeResult的方法:will be invoked after the action method but before the result execution。意思是在返回結(jié)果集前調(diào)用此方法。下面我們把public String myBefore()方法中的return LOGIN注釋掉,并讓修改此方法的返回類型為void。隨后登錄測(cè)試(注意要重新部署當(dāng)前項(xiàng)目),可以發(fā)現(xiàn)執(zhí)行結(jié)果如下:
調(diào)用myBefore方法
---調(diào)用set方法
調(diào)用execute方法
----調(diào)用myBeforeResult方法
----調(diào)用myAfter方法
從執(zhí)行的順序來(lái)看,標(biāo)記為@After的方法最后執(zhí)行,并且可以發(fā)現(xiàn):它會(huì)延時(shí)5秒執(zhí)行,但是在延時(shí)執(zhí)行時(shí),瀏覽器并沒(méi)有成功跳到success.jsp頁(yè)面,而是在5秒后,控制臺(tái)打印出myArter方法中的內(nèi)容同步跳轉(zhuǎn)到success.jsp頁(yè)面。@After :will be invoked after the action method and result execution。意為在execute方法執(zhí)行并且返回結(jié)果后此方法被調(diào)用。但是從測(cè)試來(lái)看,標(biāo)記為@After的方法是會(huì)影響到結(jié)果的返回(延時(shí)返回)。 強(qiáng)調(diào):注意方法的執(zhí)行順序,相關(guān)的內(nèi)容可以參看AnnotationWorkflowInterceptor類的api文檔。
使用PreResultListener監(jiān)聽(tīng)器,實(shí)現(xiàn)回調(diào)
PreResultListener監(jiān)聽(tīng)器對(duì)象一般是綁定在攔截器上使用。
下面我們新建struts2PreResultListener項(xiàng)目進(jìn)行測(cè)試。
步驟一,建立類,實(shí)現(xiàn)PreResultListener接口,主要代碼如下:
package com.asm;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.PreResultListener;
public class MyPreResultListener implements PreResultListener {
public void beforeResult(ActionInvocation invocation, String res) {
// System.out.println(invocation.getAction());
// System.out.println(invocation.getResultCode());
/**回調(diào)Action中的方法:
* LoginAction lg = (LoginAction) invocation.getAction(); try {
* lg.execute(); } catch (Exception e) { e.printStackTrace(); }
*/
System.out.println("檢驗(yàn)到PreResultListener被執(zhí)行");
}
}
步驟二,copy前面在自定義攔截器中用到的三個(gè)攔截器,并綁定MyPreResultListener對(duì)象,首先是在MyInterceptor類中,我們只需要修改intercept方法即可,代碼如下:
public String intercept(ActionInvocation invocation) throws Exception {
invocation.addPreResultListener(new MyPreResultListener());
System.out.println("開(kāi)始攔截");
String result = invocation.invoke();
System.out.println("結(jié)束攔截");
return result;
}
隨后在MyMethodFilterInterceptor類中作類似修改。為了區(qū)別,我們?cè)贛yAbstractInterceptor類中不綁定MyPreResultListener對(duì)象。
步驟三,編寫(xiě)struts.xml文件,主要配置內(nèi)容如下:
<struts>
<package name="interceptor" extends="struts-default">
<interceptors>
<interceptor name="myIpt" class="com.asm.MyInterceptor">
</interceptor>
<interceptor name="myAbs"
class="com.asm.MyAbstractInterceptor">
</interceptor>
<interceptor name="myMet"
class="com.asm.MyMethodFilterInterceptor">
</interceptor>
</interceptors>
<action name="login" class="com.asm.LoginAction">
<interceptor-ref name="myIpt"></interceptor-ref>
<interceptor-ref name="myAbs"></interceptor-ref>
<interceptor-ref name="myMet"></interceptor-ref>
<result name="success">/success.jsp</result>
</action>
</package>
</struts>
說(shuō)明:此實(shí)例的只是簡(jiǎn)要地演示了PreResultListener的使用,所以相對(duì)簡(jiǎn)單。對(duì)于其它相關(guān)操作,我們可以從MyPreResultListener類注釋掉的內(nèi)容中找到一此端倪。強(qiáng)調(diào):從執(zhí)行結(jié)果來(lái)看,PreResultListener對(duì)象會(huì)在返回結(jié)果前執(zhí)行,請(qǐng)注意結(jié)合攔截器執(zhí)行的順序來(lái)看。此實(shí)例目前作為了解。