Web开发最佳实践-Struts2之三拦截器(Interceptor)

一、拦截器

  • Struts2拦截器是在访问某个Action或Action的某个方法之前或之后实施拦截,并且Struts2拦截器是可插拔的,拦截器是AOP的一种实现
  • AOP:面向切面编程,其实现原理:动态代理模式
  • 拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个Action执行的前后执行的代码,也可以在一个action执行前阻止其执行。同时也提供了一种可以提取Action中可重用的代码方式
  • 拦截器栈(Interceptor Stack):Struts2拦截器栈就是将拦截器按一定的顺序连接成一条链。在访问被拦截器的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用
  • 拦截器在设计和程序结构上的优点:
    • 拦截器能把很多功能从Action中独立出来,分散到不同的拦截器里面,减少了Action的代码。拦截器和Action本身的功能都更单一了。当通用的功能代码被封装在拦截器里面(代码模块化),就可以对不同的Action,根据功能需要,来配置相应功能的拦截器了。提高了拦截器所实现的功能的重用性,也变相实现了装配式和可插拔式的体系结构,使得整个系统结构变得更灵活。

二、Struts2执行流程

Struts2执行流程.png
  • 1、客户端发送请求
  • 2、该请求经过一系列的过滤器(Filter):其中可选过滤器ActionContextCleanUp,帮助Struts2和其他框架集成。例如:SiteMesh Plugin
  • 3、接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper,来决定该请求是否需要调用某个Action
  • 4、若ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy。
  • 5、ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类
  • 6、ActionProxy创建一个ActionInvocation的实例
  • 7、ActionInvocation实例调用Action的前后,涉及到相关拦截器(Intercepter)的调用
  • 8、一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果是一个JSP或其他页面(也可以是其他的Action链)。JSP页面展现可使用Struts2框架中的标签(该过程会涉及ActionMapper)。

三、Struts2内置拦截器

        <interceptors>
            <interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
            <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>
            <interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/>
            <interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
            <interceptor name="cookie" class="org.apache.struts2.interceptor.CookieInterceptor"/>
            <interceptor name="cookieProvider" class="org.apache.struts2.interceptor.CookieProviderInterceptor"/>
            <interceptor name="clearSession" class="org.apache.struts2.interceptor.ClearSessionInterceptor" />
            <interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor" />
            <interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor" />
            <interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/>
            <interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>
            <interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
            <interceptor name="i18n" class="org.apache.struts2.interceptor.I18nInterceptor"/>
            <interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/>
            <interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
            <interceptor name="scopedModelDriven" class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"/>
            <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
            <interceptor name="paramRemover" class="com.opensymphony.xwork2.interceptor.ParameterRemoverInterceptor"/>
            <interceptor name="actionMappingParams" class="org.apache.struts2.interceptor.ActionMappingParametersInterceptor"/>
            <interceptor name="prepare" class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/>
            <interceptor name="staticParams" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/>
            <interceptor name="scope" class="org.apache.struts2.interceptor.ScopeInterceptor"/>
            <interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>
            <interceptor name="timer" class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/>
            <interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>
            <interceptor name="tokenSession" class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/>
            <interceptor name="validation" class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/>
            <interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/>
            <interceptor name="store" class="org.apache.struts2.interceptor.MessageStoreInterceptor" />
            <interceptor name="checkbox" class="org.apache.struts2.interceptor.CheckboxInterceptor" />
            <interceptor name="datetime" class="org.apache.struts2.interceptor.DateTextFieldInterceptor" />
            <interceptor name="profiling" class="org.apache.struts2.interceptor.ProfilingActivationInterceptor" />
            <interceptor name="roles" class="org.apache.struts2.interceptor.RolesInterceptor" />
            <interceptor name="annotationWorkflow" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor" />
            <interceptor name="annotationParameterFilter" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationParameterFilterInterceptor" />
            <interceptor name="multiselect" class="org.apache.struts2.interceptor.MultiselectInterceptor" />
            <interceptor name="noop" class="org.apache.struts2.interceptor.NoOpInterceptor" />

            <!-- Empty stack - performs no operations -->
            <interceptor-stack name="emptyStack">
                <interceptor-ref name="noop"/>
            </interceptor-stack>

            <!-- Basic stack -->
            <interceptor-stack name="basicStack">
                <interceptor-ref name="exception"/>
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="checkbox"/>
                <interceptor-ref name="datetime"/>
                <interceptor-ref name="multiselect"/>
                <interceptor-ref name="actionMappingParams"/>
                <interceptor-ref name="params"/>
                <interceptor-ref name="conversionError"/>
            </interceptor-stack>

            <!-- Sample validation and workflow stack -->
            <interceptor-stack name="validationWorkflowStack">
                <interceptor-ref name="basicStack"/>
                <interceptor-ref name="validation"/>
                <interceptor-ref name="workflow"/>
            </interceptor-stack>

            <!-- Sample file upload stack -->
            <interceptor-stack name="fileUploadStack">
                <interceptor-ref name="fileUpload"/>
                <interceptor-ref name="basicStack"/>
            </interceptor-stack>

            <!-- Sample model-driven stack  -->
            <interceptor-stack name="modelDrivenStack">
                <interceptor-ref name="modelDriven"/>
                <interceptor-ref name="basicStack"/>
            </interceptor-stack>

            <!-- Sample action chaining stack -->
            <interceptor-stack name="chainStack">
                <interceptor-ref name="chain"/>
                <interceptor-ref name="basicStack"/>
            </interceptor-stack>

            <!-- Sample i18n stack -->
            <interceptor-stack name="i18nStack">
                <interceptor-ref name="i18n"/>
                <interceptor-ref name="basicStack"/>
            </interceptor-stack>

            <!-- An example of the paramsPrepareParams trick. This stack
                 is exactly the same as the defaultStack, except that it
                 includes one extra interceptor before the prepare interceptor:
                 the params interceptor.

                 This is useful for when you wish to apply parameters directly
                 to an object that you wish to load externally (such as a DAO
                 or database or service layer), but can't load that object
                 until at least the ID parameter has been loaded. By loading
                 the parameters twice, you can retrieve the object in the
                 prepare() method, allowing the second params interceptor to
                 apply the values on the object. -->
            <interceptor-stack name="paramsPrepareParamsStack">
                <interceptor-ref name="exception"/>
                <interceptor-ref name="alias"/>
                <interceptor-ref name="i18n"/>
                <interceptor-ref name="checkbox"/>
                <interceptor-ref name="datetime"/>
                <interceptor-ref name="multiselect"/>
                <interceptor-ref name="params"/>
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="chain"/>
                <interceptor-ref name="modelDriven"/>
                <interceptor-ref name="fileUpload"/>
                <interceptor-ref name="staticParams"/>
                <interceptor-ref name="actionMappingParams"/>
                <interceptor-ref name="params"/>
                <interceptor-ref name="conversionError"/>
                <interceptor-ref name="validation">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="workflow">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
            </interceptor-stack>

            <!-- A complete stack with all the common interceptors in place.
                 Generally, this stack should be the one you use, though it
                 may do more than you need. Also, the ordering can be
                 switched around (ex: if you wish to have your servlet-related
                 objects applied before prepare() is called, you'd need to move
                 servletConfig interceptor up.

                 This stack also excludes from the normal validation and workflow
                 the method names input, back, and cancel. These typically are
                 associated with requests that should not be validated.
                 -->
            <interceptor-stack name="defaultStack">
                <interceptor-ref name="exception"/>
                <interceptor-ref name="alias"/>
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="i18n"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="chain"/>
                <interceptor-ref name="scopedModelDriven"/>
                <interceptor-ref name="modelDriven"/>
                <interceptor-ref name="fileUpload"/>
                <interceptor-ref name="checkbox"/>
                <interceptor-ref name="datetime"/>
                <interceptor-ref name="multiselect"/>
                <interceptor-ref name="staticParams"/>
                <interceptor-ref name="actionMappingParams"/>
                <interceptor-ref name="params"/>
                <interceptor-ref name="conversionError"/>
                <interceptor-ref name="validation">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="workflow">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="debugging"/>
            </interceptor-stack>

            <!-- The completeStack is here for backwards compatibility for
                 applications that still refer to the defaultStack by the
                 old name -->
            <interceptor-stack name="completeStack">
                <interceptor-ref name="defaultStack"/>
            </interceptor-stack>

            <!-- Sample execute and wait stack.
                 Note: execAndWait should always be the *last* interceptor. -->
            <interceptor-stack name="executeAndWaitStack">
                <interceptor-ref name="execAndWait">
                    <param name="excludeMethods">input,back,cancel</param>
                </interceptor-ref>
                <interceptor-ref name="defaultStack"/>
                <interceptor-ref name="execAndWait">
                    <param name="excludeMethods">input,back,cancel</param>
                </interceptor-ref>
            </interceptor-stack>

       </interceptors>

1、常见拦截器

1:params拦截器
这个拦截器偷偷的把请求参数设置到相应的Action的属性去的,并自动进行类型转换。

2.modelDriven拦截器
如果Action实现ModelDriven接口,它将getModel()取得的模型对象存入OgnlValueStack中。

3.execption拦截器
顾名思义,在抛出异常的时候,这个拦截器起作用。最好把它放在第一位,让它能捕获所有的异常。

4.validation拦截器
调用验证框架读取 *-validation.xml文件,并且应用在这些文件中声明的校验。

5.token拦截器
核对当前Action请求(request)的有效标识,防止重复提交Action请求。

6.fileUpload拦截器
用来处理文件上传

7.workflow拦截器
调用Action的validate方法,一旦有错误返回,重新定位到INPUT结果视图

8.servletConfig
通过感知接口,获取感应对象

9.store:保证在两次请求之间共享数据的.

2、servletConfig拦截器原理

  • 实现ServletRequestAware接口
package com.revanwang.servlet;


import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//1、通过让Action类去实现感知接口.
public class ActionServlet1 extends ActionSupport implements
        ServletRequestAware, //获取 HttpServletRequest 对象
        ServletResponseAware
{

    private HttpServletRequest request;
    private HttpServletResponse response;

    @Override
    public void setServletRequest(HttpServletRequest httpServletRequest) {
        this.request = httpServletRequest;
    }

    @Override
    public void setServletResponse(HttpServletResponse httpServletResponse) {
        this.response = httpServletResponse;
    }

    @Override
    public String execute() throws Exception {
        System.out.println(this.request.getParameter("name")+"--"+this.response);
        return NONE;
    }

}
  • 从struts2-core-2.5.20.jar中struts-default.xml中
<interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>
  • ServletConfigInterceptor源码
public class ServletConfigInterceptor extends AbstractInterceptor implements StrutsStatics {
    private static final long serialVersionUID = 605261777858676638L;

    public ServletConfigInterceptor() {
    }

    public String intercept(ActionInvocation invocation) throws Exception {
        Object action = invocation.getAction();
        ActionContext context = invocation.getInvocationContext();
        HttpServletRequest request;
        if (action instanceof ServletRequestAware) {
            request = (HttpServletRequest)context.get("com.opensymphony.xwork2.dispatcher.HttpServletRequest");
            ((ServletRequestAware)action).setServletRequest(request);
        }

        if (action instanceof ServletResponseAware) {
            HttpServletResponse response = (HttpServletResponse)context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse");
            ((ServletResponseAware)action).setServletResponse(response);
        }

        if (action instanceof ParameterAware) {
            context.getParameters().applyParameters((ParameterAware)action);
        }

        if (action instanceof HttpParametersAware) {
            ((HttpParametersAware)action).setParameters(context.getParameters());
        }

        if (action instanceof ApplicationAware) {
            ((ApplicationAware)action).setApplication(context.getApplication());
        }

        if (action instanceof SessionAware) {
            ((SessionAware)action).setSession(context.getSession());
        }

        if (action instanceof RequestAware) {
            ((RequestAware)action).setRequest((Map)context.get("request"));
        }

        if (action instanceof PrincipalAware) {
            request = (HttpServletRequest)context.get("com.opensymphony.xwork2.dispatcher.HttpServletRequest");
            if (request != null) {
                ((PrincipalAware)action).setPrincipalProxy(new ServletPrincipalProxy(request));
            }
        }

        if (action instanceof ServletContextAware) {
            ServletContext servletContext = (ServletContext)context.get("com.opensymphony.xwork2.dispatcher.ServletContext");
            ((ServletContextAware)action).setServletContext(servletContext);
        }

        return invocation.invoke();
    }
}
  • 执行过程
        Object action = invocation.getAction();
        ActionContext context = invocation.getInvocationContext();
        HttpServletRequest request;
        if (action instanceof ServletRequestAware) {
            request = (HttpServletRequest)context.get("com.opensymphony.xwork2.dispatcher.HttpServletRequest");
            ((ServletRequestAware)action).setServletRequest(request);
        }

1:通过invocation.getAction()来获取自定义Action类,然后在判断该Action类是否实现ServletRequestAware接口,如果实现ServletRequestAware接口,就会调用自定义Action类中的setServletRequest方法

四、在Struts2中自定义拦截器
通过(登录检查拦截器,访问某一个页面时必须要先登录,登录之后才能访问)例子来实现自定义拦截器

  • login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>注册界面</title>
</head>
<body>
    <form action="/interceptorLogin" method="post">
        用户名:<input type="text" name="user.userName">
        <br>
        密码  :<input type="text" name="user.passWord">
        <br>
        <input type="submit" value=" 注 册 ">
    </form>
</body>
</html>
  • welcome.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录成功</title>
</head>
<body>
        欢迎${sessionScope.user.userName}
</body>
</html>
  • User
package com.revanwang.interceptor;

import lombok.Data;

@Data
public class User {
    private String userName;
    private String passWord;
}
  • Action类
package com.revanwang.interceptor;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import lombok.Getter;
import lombok.Setter;

public class LoginAction extends ActionSupport {

    @Setter
    @Getter
    private User user;  //获取请求参数

    @Override
    public String execute() throws Exception {
        System.out.println("Login....." + this.user);
        //保存user数据到 session 中
        ActionContext.getContext().getSession().put("user", this.user);
        return SUCCESS;
    }
}
  • struts.xml
<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
        "http://struts.apache.org/dtds/struts-2.5.dtd">


<struts>
    <package name="default" extends="struts-default" namespace="/">
        <action name="interceptorLogin" class="com.revanwang.interceptor.LoginAction">
            <result>/login/welcome.jsp</result>
        </action>
    </package>

</struts>
  • 上面的缺点就是无论是否登陆成功都可以访问到welcome.jsp,这显然不符合需求。为了只能在登录成功后才能访问welcome.jsp.

    • 1、直接判断Action中的请求参数,但是这种方法存在硬编码
    • 2、自定义拦截器(checkLoginInterceptor)
  • CheckLoginInterceptor

package com.revanwang.interceptor;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

import java.util.Map;

public class CheckLoginInterceptor extends AbstractInterceptor {
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {

        Map<String, Object> sessionMap = invocation.getInvocationContext().getSession();
        User user = (User) sessionMap.get("user");
        System.out.println("CheckLoginInterceptor. . . ." +  sessionMap + "\n" + user);
        if (user == null || user.getUserName().equals("".trim())) {
        System.out.println("CheckLoginInterceptor. . . .empy");
            return "login";
        }
        return invocation.invoke();
    }
}
  • struts.xml
<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
        "http://struts.apache.org/dtds/struts-2.5.dtd">

<struts>

    <package name="default" extends="struts-default" namespace="/">
        <!--1、注册拦截器-->
        <interceptors>
            <!-- 自定义检测登录拦截器 -->
            <interceptor name="checkLogin" class="com.revanwang.interceptor.CheckLoginInterceptor"></interceptor>

            <!--  自定义拦截器栈
                  包含 拦截器 / 拦截器栈
            -->
            <interceptor-stack name="myStack">
                <interceptor-ref name="checkLogin"/>
                <interceptor-ref name="defaultStack"/>
            </interceptor-stack>

        </interceptors>


        <!--  2、设置当前package的默认引用拦截器  -->
        <default-interceptor-ref name="myStack"/>

        <!--  3、全局的结果视图 -->
        <global-results>
            <result name="login" type="redirect">
                /login/login.jsp
            </result>
        </global-results>

        <!--  4、Action  -->
        <!--  LoginAction
            redirectAction: 跳转 checkAction Action
        -->
        <action name="interceptorLogin" class="com.revanwang.interceptor.LoginAction">
            <!-- 引用默认的拦截器
                对于LoginAction,不需要做登录检查,但是需要获取请求参数
            -->
            <interceptor-ref name="defaultStack"/>
            <result type="redirectAction">checkAction</result>
        </action>

        <!--
            自定义了一个添加 拦截器 的action
            该 checkAction Action默认继承com.opensymphony.xwork2.ActionSupport
            默认方法 excetue
            默认回调 SUCCESS
        -->
        <action name="checkAction">
            <result>/login/welcome.jsp</result>
        </action>

    </package>

</struts>

3、拦截器设置参数

  • CheckLoginInterceptor
package com.revanwang.interceptor;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

import java.util.Arrays;
import java.util.Map;

public class CheckLoginInterceptor extends AbstractInterceptor {

    //需要放行的action名称
    private String[] unCheckedActionNames = {};

    //通过拦截器设置对应参数
    public void setActionNames(String actionNames) {
        if (actionNames != null) {
            this.unCheckedActionNames = actionNames.split(",");
        }
    }

    @Override
    public String intercept(ActionInvocation invocation) throws Exception {

        //拦截器放行
        String actionName = invocation.getProxy().getActionName();
        System.out.println("unCheckedActionNames:===" + actionName);
        if (Arrays.asList(this.unCheckedActionNames).contains(actionName)) {
            System.out.println("unCheckedActionNames:===" + actionName);
            return invocation.invoke();
        }

        Map<String, Object> sessionMap = invocation.getInvocationContext().getSession();
        User user = (User) sessionMap.get("user");
        if (user == null || user.getUserName().equals("".trim())) {
            System.out.println("CheckLoginInterceptor. . . .empy");
            return "login";
        }
        return invocation.invoke();
    }
}
  • struts.xml
<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
        "http://struts.apache.org/dtds/struts-2.5.dtd">

<struts>

    <package name="default" extends="struts-default" namespace="/">
        <!--1、注册拦截器-->
        <interceptors>
            <!-- 自定义检测登录拦截器 -->
            <interceptor name="checkLogin" class="com.revanwang.interceptor.CheckLoginInterceptor">
                <!--放行拦截器的名称-->
                <param name="actionNames">lt,dl</param>
            </interceptor>

            <!--  自定义拦截器栈
                  包含 拦截器 / 拦截器栈
            -->
            <interceptor-stack name="myStack">
                <interceptor-ref name="checkLogin"/>
                <interceptor-ref name="defaultStack"/>
            </interceptor-stack>

        </interceptors>

        <!--  2、设置当前package的默认引用拦截器  -->
        <default-interceptor-ref name="myStack"/>

        <!--  3、全局的结果视图 -->
        <global-results>
            <result name="login" type="redirect">
                /login/login.jsp
            </result>
        </global-results>

        <!--  4、Action  -->
        <!--  LoginAction
            redirectAction: 跳转 checkAction Action
        -->
        <action name="interceptorLogin" class="com.revanwang.interceptor.LoginAction">
            <!-- 引用默认的拦截器
                对于LoginAction,不需要做登录检查,但是需要获取请求参数
            -->
            <interceptor-ref name="defaultStack"/>
            <result type="redirectAction">checkAction</result>
        </action>

        <action name="lt" class="com.revanwang.interceptor.LoginAction" method="loginList">
<!--            <result type="redirectAction">checkAction</result>-->
            <interceptor-ref name="defaultStack"/>
            <result name="list" type="dispatcher">/WEB-INF/JSP/list.jsp</result>
        </action>

        <action name="dl" class="com.revanwang.interceptor.LoginAction" method="loginDelete">
            <result name="delete" type="dispatcher">/login/welcome.jsp</result>
        </action>

        <action name="sv" class="com.revanwang.interceptor.LoginAction" method="loginSave">
            <result name="save" type="redirect">
                <param name="location">/login/welcome.jsp</param>
            </result>
        </action>

        <!--
            自定义了一个添加 拦截器 的action
            该 checkAction Action默认继承com.opensymphony.xwork2.ActionSupport
            默认方法 excetue
            默认回调 SUCCESS
        -->
        <action name="checkAction">
            <result>/login/welcome.jsp</result>
        </action>

    </package>

</struts>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容