Struts2攔截器Interceptor


攔截器是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í)例目前作為了解。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,976評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,781評論 18 139
  • 概述 什么是Struts2的框架Struts2是Struts1的下一代產(chǎn)品,是在 struts1和WebWork的...
    inke閱讀 2,269評論 0 50
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,717評論 18 399
  • spring mvc 工作機(jī)制(原理): DispatcherServlet主要用作職責(zé)調(diào)度工作,本身主要用于控制...
    java大濕兄閱讀 1,912評論 5 24
  • 傷感是一顆流彈,抽不冷被擊中。抑或是在最沒防備的時(shí)候,穿著新鞋踩了一腳屎。事實(shí)證明,人為制造高潮是行不通的。事實(shí)還...
    picspin閱讀 521評論 0 2