攔截器是Struts2框架的核心,它主要完成解析請求參數(shù)、將請求參數(shù)賦值給Action屬性、執(zhí)行數(shù)據(jù)校驗(yàn)、文件上傳等工作。Struts2設(shè)計(jì)的靈巧性,攔截器起了關(guān)鍵性的作用,當(dāng)需要擴(kuò)展Struts2功能時(shí),只需要提供對應(yīng)攔截器,并將它配置在Struts2容器中即可;如果不需要該功能時(shí),也只需要取消該攔截器的配置即可。
Struts2內(nèi)建了大量的攔截器,這些攔截器以name-class對的形式配置在struts-default. xml文件中,其中name是攔截器的名字,就是以后我們使用該攔截器的唯一標(biāo)識;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接口。重寫String intercept(ActionInvocation invocation)方法。
package test002Iterceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
/**
* Created by yangcs on 2017/2/1.
* 通過實(shí)現(xiàn)Interceptor接口,重寫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("開始攔截");
String result = actionInvocation.invoke(); //invoke()方法會把請求傳遞到下一個(gè)攔截器或者最終的action中
System.out.println("結(jié)束攔截");
return result;
}
}
為了使用此攔截器,我們必須將此攔截器進(jìn)行注冊,隨后再在要使用此攔截器的Action中引用。即首先在<package>中注冊,內(nèi)容如下:
<interceptors>
<interceptor name="login001" class="test002Iterceptor.Iterceptor001"></interceptor>
</interceptors>
注冊完成后,如果我們要在login.action中使用此攔截器,只需要在<action>中增加如下內(nèi)容:
<interceptor-ref name="login001"></interceptor-ref>
實(shí)例流程分析:當(dāng)我們?yōu)長oginAction配置了攔截器時(shí),并且有客戶端請求此Action時(shí),會首先被此攔截器攔住,然后執(zhí)行System.out.println("開始攔截"),隨后我們調(diào)用invocation.invoke()方法,它會把請求繼續(xù)傳遞給下一個(gè)攔截器,下一個(gè)攔截器也會繼續(xù)執(zhí)行相應(yīng)代碼后再調(diào)用invoke方法繼續(xù)傳遞,直到請求到達(dá)最后一個(gè)攔截器,它會把請求傳遞給Action,比如,我們這里只用到了一個(gè)攔截器,當(dāng)它執(zhí)行完成后,會把請求直接轉(zhuǎn)交到LoginAction處理,LoginAction處理完成后,它會返回結(jié)果給MyInterceptor攔截器。
方式二、繼承AbstractInterceptor抽象類,重寫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抽象類,重寫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)聽器,則配置了此攔截器的action在發(fā)送result前,會調(diào)用監(jiān)聽器中的beforeResult()方法!
System.out.println("AbstractIntorceptor開始攔截");
String result = actionInvocation.invoke();
System.out.println("AbstractIntorceptor結(jié)束攔截");
return result;
}
}
然后注冊此攔截器,在<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類,重寫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類,重寫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開始攔截");
String result = actionInvocation.invoke();
System.out.println("MethodFilterInterceptor結(jié)束攔截");
return result;
}
}
然后注冊此攔截器,在<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ì)便為LoginAction配置了三個(gè)攔截器,當(dāng)我們點(diǎn)擊登錄時(shí)會在控制臺打印出如下語句:
開始攔截
Abs開始攔截
method開始攔截
--先執(zhí)行攔截器,再執(zhí)行此Action
method結(jié)束攔截
Abs結(jié)束攔截
結(jié)束攔截
攔截器的執(zhí)行順序和過濾器filter一樣,取決于在action標(biāo)簽中的配置順序
其實(shí)當(dāng)我們點(diǎn)擊登錄時(shí),本來是要訪問LoginAction,最后會把LoginAction的執(zhí)行結(jié)果傳遞給訪問者。但是當(dāng)我們配置了攔截器時(shí),當(dāng)我們?nèi)ピL問Action時(shí),會首先被攔截,隨后攔截器執(zhí)行一些操作后才會繼續(xù)把請求傳遞下去。
下圖說明攔截流程:
注:自定義攔截器需要特別注意的是不要忘記引入struts2默認(rèn)的攔截器,可以使用攔截器棧(Interceptor Stack)來組合多個(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)的攔截器就會失效,必須手動添加配置,否則就會有問題-->
</interceptor-stack>
</interceptors>
上面分別使用了三種方式來創(chuàng)建自定義的攔截器,第一種方式是最原始的實(shí)現(xiàn)方式(實(shí)現(xiàn)Interceptor接口),第二種方式的好處是我們可以不必重寫所有的方法(繼承AbstractInterceptor抽象類),較常用。第三種方式進(jìn)行了擴(kuò)展(繼承MethodFilterInterceptor類),可以更靈活地對action中不同的方法進(jìn)行單獨(dú)的攔截:
使用來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;
}
}
步驟二、注冊此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配置了前面寫的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í)會訪問到m1Suc.jsp頁面, 點(diǎn)m2、m3會分別訪問到m2Suc.jsp、m3Suc.jsp頁面。現(xiàn)在假如我們想訪問m2、m3時(shí)不被攔截,我們只需修改MyMethodFilterInterceptor注冊:修改內(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。除了這種在注冊攔截器時(shí)指定攔截外,還可以在引用攔截器時(shí)指定,即如下形式:
<interceptor-ref name="myMet">
<param name="excludeMethods">m2,m3</param>
<param name="includeMethods">m1</param>
</interceptor-ref>
上面的兩處<param>配置是等價(jià)的,但是如果〈param〉配置沖突,誰起作用?即如果我們對m1配置了excludeMethods同時(shí)又配置了includeMethods時(shí),誰起作用,我們可以進(jìn)行這些沖突的驗(yàn)證。以下是驗(yàn)證結(jié)果:
引用配置(在Action引用攔截器時(shí)配置)時(shí),以includeMethods的配置為準(zhǔn)。
一旦我們?yōu)閿r截器使用了<param>配置,而對m1這樣的方法不配置任何,就不會被攔截。
但是如果不使用<param>,它們?nèi)慷家粩r截。
注冊配置時(shí)(在注冊攔截器時(shí)配置),情況和“引用配置”完全一樣。
引用配置和注冊配置沖突時(shí),以引用配置為準(zhǔn)。
使用默認(rèn)的execAndWait攔截器實(shí)現(xiàn)查詢等待效果
當(dāng)我們進(jìn)行數(shù)據(jù)庫查詢等相關(guān)的操作時(shí),如果服務(wù)器負(fù)荷過重可能不能及時(shí)把數(shù)據(jù)查詢出來,進(jìn)而會在狀態(tài)攔顯示“正在打開...”,但卻一直轉(zhuǎn)不到相關(guān)的頁面,這將給客戶端帶來不便,甚于很多人會因此不愿使用網(wǎng)站的所有服務(wù)。對此我們可以在客戶提交時(shí),馬上轉(zhuǎn)到一個(gè)頁面,并在該頁面顯示“您的請求已提交,服務(wù)器正在查詢,請等待...”的內(nèi)容,這樣客戶將不會陷于無賴的等待中。
對于此要求,struts2可以輕松幫我們完成。
下面新建struts2wait項(xiàng)目演示此實(shí)例。
建立LoginAction,代碼如下:
package com.asm;
public class LoginAction extends ActionSupport {
public String execute() throws Exception {
Thread.sleep(5000);
return SUCCESS;
}
}
說明:為了模擬服務(wù)器負(fù)荷過重,查詢時(shí)間要很長。我們在使用了線程休眠的方式。
隨后配置此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攔截器,如需要配置此攔截器,此攔截器一定要配置在最后,否則會出現(xiàn)一些難預(yù)知的結(jié)果。如果使用此攔截器,我們通常還會配置wait的result結(jié)果集,因?yàn)楫?dāng)我們請求的Action在未執(zhí)行完,就是未返回結(jié)果時(shí),會首先把wait result返回,而在wait result所指定的頁面中通常會再次發(fā)送請求給原始的Action。所以wait.jsp的主要內(nèi)容如下:
<head>
<meta http-equiv="refresh" content="1;login.action">
</head>
<body> 查詢請求已提交,正在查詢數(shù)據(jù),請等待... </body>
在此頁面中,我們指定了每隔1秒便發(fā)送請求到login.action中去。這樣,客戶端便可以及時(shí)獲取查詢結(jié)果。結(jié)合此實(shí)例,我們簡要分析流程:當(dāng)我們發(fā)出請求到此Login.Action中去時(shí),首先會被exeAndWait攔截器攔截到,這樣它便跳轉(zhuǎn)到wait.jsp頁面,在wait.jsp頁面中每隔1秒我們會繼續(xù)發(fā)送此Action的請求,當(dāng)再次請求到達(dá)LoginAction時(shí),如果它已經(jīng)返回,則會跳到此Action返回的頁面,如果LoginAction未返回,則繼續(xù)停留在wait.jsp中,再隔1秒又再次發(fā)送請求到LoginAction中去。
其實(shí)如果服務(wù)器能很快查詢出結(jié)果,我們則不需要用到wait.jsp頁面,我們只需在<interceptor-ref name="execAndWait"></interceptor-ref>中增加如下一段配置:
<param name="delay">6000</param>
這樣便延遲請求到達(dá)wait.jsp頁面,這樣當(dāng)請求到達(dá)時(shí)它會在LoginAction中執(zhí)行6秒時(shí)間再到wait.jsp,而6秒LoginAction足以執(zhí)行完并返回結(jié)果,所以當(dāng)攔截器
執(zhí)行時(shí)首先檢查到此Action已經(jīng)返回結(jié)果。則攔截器會直接用此返回頁面,如果此時(shí)發(fā)現(xiàn)LoginAction并未執(zhí)行完,它便會把wait resutl指定的頁面返回。
使用默認(rèn)的TokenInterceptor攔截器防止表單重復(fù)提交
由于某些原因,用戶在進(jìn)行類似表單提交的操作后,以為表單未被提交,會進(jìn)行多次的重復(fù)提交。為了避免用戶多次提交給服務(wù)器帶來負(fù)荷。我們會對表單提交這樣的操作進(jìn)行一些處理,以告訴用戶不要重復(fù)提交。
下面我們建立struts2token項(xiàng)目,使用struts2的token攔截器來實(shí)現(xiàn)此案例(也可以通過自己在session中生成token令牌來實(shí)現(xiàn)這一效果:使用Session防止表單重復(fù)提交)。
步驟一,編寫login.jsp頁面,內(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>
說明,此登錄頁面中的關(guān)鍵技術(shù)就是使用了標(biāo)簽庫中的<s:token></s:token>標(biāo)簽,它的作用就是在用戶訪問此頁面時(shí)會生成一個(gè)sessionId,在提交時(shí)會服務(wù)器會據(jù)此驗(yàn)證表單是否已提交。“To 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ù)提交。
步驟二,編寫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>
說明:在此Action下,我們配置了token攔截器,另注意到在此Action下我們還配置了一個(gè)“invalid.token”result,因?yàn)樘峤粫r(shí)服務(wù)器如果根據(jù)token標(biāo)簽產(chǎn)生的sessionId判斷出表單已提交,它則返回invalid.token指向的視圖。比如這里,如果重復(fù)提交則會轉(zhuǎn)到.../subError.jsp中去。另不要忘記了引入默認(rèn)的攔截器棧。補(bǔ)充:關(guān)于token攔截器更多細(xì)節(jié)可以訪問org.apache.struts2.interceptor.TokenInterceptor類的api說明。
步驟四,編寫配置中所用到j(luò)sp頁面,這些頁面編寫簡單,在此省去。
步驟五、發(fā)布測試,請注意訪問login.jsp頁面時(shí),查看源文件時(shí)會發(fā)現(xiàn)增加了兩個(gè)隱藏域信息。
步驟六、更換攔截器:我們還可以使用tokenSession攔截器,它的功能比上面的增強(qiáng),它能保證持有相同sessionId的并發(fā)請求等待第一個(gè)完成之后才能被提交處理,但是它返回的是action執(zhí)行后的result.接著上例,我們只需要在配置中作如下修改:把上面的token攔截器改成
<interceptor-ref name="tokenSession"></interceptor-ref>
即可。隨后便可以測試,測試時(shí)會發(fā)現(xiàn)如果我們重復(fù)提交,它總是返回到上一次的success.jsp頁面,但是它并不是經(jīng)過LoginAction中的execute處理后返回(我們System.out.print語句在重復(fù)提交時(shí)并未打印出來),而是此攔截器判斷出是重復(fù)后直接返回上一次提交轉(zhuǎn)向的頁面。
使用攔截器實(shí)現(xiàn)權(quán)限驗(yàn)證
為了說明此問題,我們建立struts2auth項(xiàng)目,流程圖如下:
簡短說明:當(dāng)我們訪問main.jsp頁面,并試圖通過此頁面中的鏈接地址:note.action來訪問到.../WEB-INF/note.jsp頁面時(shí),由于訪問的note.action配置了攔截器,所以會被攔截,如果攔截器判斷登錄則可以訪問,否則會跳到登錄頁面。如果我們從登錄頁面直接到main.jsp頁面,再來訪問note.action時(shí),同樣被攔截但是由于登錄過,所以可以訪問到此action對應(yīng)的內(nèi)容。由這里的分析可以看出關(guān)鍵點(diǎn)就登錄成功時(shí)給出標(biāo)志提供給攔截器判斷是否成功登錄。
步驟一,搭建好相關(guān)的開發(fā)環(huán)境,并準(zhǔn)備好登錄頁面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;
}
}
...省略username的get/set方法
}
說明:我們這里是設(shè)定了只有登錄用戶名為admin時(shí),此Action才設(shè)置登錄標(biāo)志。另這里獲取Session對象采取的是“與Servlet解耦合的非IOC方式”。
步驟三,編寫攔截器類,代碼如下:
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>
說明:結(jié)合前面的一些代碼來看,當(dāng)我們?yōu)閚ote.action配置了前面寫所的AuthInterceptor攔截器時(shí),如果我們要訪問note.action,攔截器會首先判斷是否登錄,如果登錄則繼續(xù)把請求傳遞下去,如果沒有登錄則會返回到登錄頁面。
使用默認(rèn)的AnnotationWorkflowInterceptor攔截器為action方法添加注解調(diào)用
AnnotationWorkflowInterceptor此攔截器可以調(diào)用在Action中任何有注解的方法。下面我們來演示它的使用,具體步驟如下:
步驟一,建立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;
}
}
說明:要想使用方法成為被攔截器監(jiān)視的注解方法,只需在方法關(guān)加上@...這樣的形式并導(dǎo)入相關(guān)的類即可。
步驟二,編寫相關(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é)合配置說明:當(dāng)我們?yōu)長oginAction配置了AnnotationWorkflowInterceptor攔截器時(shí),LoginAction中的所有注解方法才真正生效。下面重點(diǎn)是來討論這些方法的執(zhí)行順序及作用。
加@Before注解的方法意思是在action的execute方法執(zhí)行之前被調(diào)用,但是此方法如果返回不為空的話,它的返回結(jié)果將是真正的返回結(jié)果,比如這里我們r(jià)eturn LOGIN,這樣無論以什么用戶名登錄,它總會返回到login result(這里為login.jsp頁面) 。但是從執(zhí)前結(jié)果來看,在返回前仍執(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。隨后登錄測試(注意要重新部署當(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í)行的順序來看,標(biāo)記為@After的方法最后執(zhí)行,并且可以發(fā)現(xiàn):它會延時(shí)5秒執(zhí)行,但是在延時(shí)執(zhí)行時(shí),瀏覽器并沒有成功跳到success.jsp頁面,而是在5秒后,控制臺打印出myArter方法中的內(nèi)容同步跳轉(zhuǎn)到success.jsp頁面。@After :will be invoked after the action method and result execution。意為在execute方法執(zhí)行并且返回結(jié)果后此方法被調(diào)用。但是從測試來看,標(biāo)記為@After的方法是會影響到結(jié)果的返回(延時(shí)返回)。 強(qiáng)調(diào):注意方法的執(zhí)行順序,相關(guān)的內(nèi)容可以參看AnnotationWorkflowInterceptor類的api文檔。
使用PreResultListener監(jiān)聽器,實(shí)現(xiàn)回調(diào)
PreResultListener監(jiān)聽器對象一般是綁定在攔截器上使用。
下面我們新建struts2PreResultListener項(xiàng)目進(jìn)行測試。
步驟一,建立類,實(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對象,首先是在MyInterceptor類中,我們只需要修改intercept方法即可,代碼如下:
public String intercept(ActionInvocation invocation) throws Exception {
invocation.addPreResultListener(new MyPreResultListener());
System.out.println("開始攔截");
String result = invocation.invoke();
System.out.println("結(jié)束攔截");
return result;
}
隨后在MyMethodFilterInterceptor類中作類似修改。為了區(qū)別,我們在MyAbstractInterceptor類中不綁定MyPreResultListener對象。
步驟三,編寫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>
說明:此實(shí)例的只是簡要地演示了PreResultListener的使用,所以相對簡單。對于其它相關(guān)操作,我們可以從MyPreResultListener類注釋掉的內(nèi)容中找到一此端倪。強(qiáng)調(diào):從執(zhí)行結(jié)果來看,PreResultListener對象會在返回結(jié)果前執(zhí)行,請注意結(jié)合攔截器執(zhí)行的順序來看。此實(shí)例目前作為了解。