一、拦截器
1、拦截器:拦截器和过滤器很相似。在 action 执行的前后执行。Struts2 的核心功能都是通过拦截器来实现。
2、拦截器栈:由多个拦截器组成。
3、作用:对于 action 的一些公共处理代码可以放到拦截器中来实现。如:权限控制,日志等等。
4、多个拦截器之间的执行是采用责任链设计模式来实现。
5、拦截器的执行流程
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5CMzMzNxETM1EDMxkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
6.自定义拦截器的实现步骤:
1)编写拦截器(实现Interceptor接口或者继承AbstractInterceptor类)
2)在structs.xml中配置拦截器
3)在action中引用拦截器
模拟structs2默认拦截器---timer,自定义拦截器实现
/*** *计算 action 执行时间 */
public class TimeInterceptor extends AbstractInterceptor{
@Override
public String intercept(ActionInvocation invocation) throws Exception {
long start=System.currentTimeMillis();
//执行下一个拦截器,当拦截器执行完后执行
action String result= invocation.invoke();
long end=System.currentTimeMillis();
System.out.println("执行该 Action 所用时间为: "+(end-start)+"ms");
return result;
}
}
在structs.xml中配置
<struts> <package name="default" extends="struts-default" namespace="/"> <!-- 配置拦截器 --> <interceptors> <interceptor name="time" class="cn.sxt.interceptor.TimeInterceptor"/> </interceptors> <action name="hello" class="cn.sxt.action.HelloAction"> <result>/index.jsp</result> <!-- 应用拦截器 --> <interceptor-ref name="time"/> </action> </package> </struts> |
当请求 hello.action 时将会执行该拦截器。
8、拦截器的配置详解
1)当引用了自定义拦截器后,默认的拦截器将不会生效,默认拦截器:在 struts-default.xml 中,当配置默认拦截器以后, 如果不引用拦截器,那么默认的拦截器将起作用。
在structs-default.xml中的声明
<default-interceptor-ref name="defaultStack"/> |
2)当引用自定义拦截器后,又想使用 struts2 提供的拦截器功能,那么需要手动引用
<action name="hello" class="cn.sxt.action.HelloAction"> <result>/index.jsp</result> <!-- 引用拦截器 --> <interceptor-ref name="time"/> <!-- 引用默认的拦截器栈 ;引用拦截器和引用拦截器栈的方式一样--> <interceptor-ref name="defaultStack"/> </action> |
3)当 action 引用的拦截器个数比较多时,可以自定义拦截器栈,将多个拦截器放入一个拦截器栈中。
<!-- 一个拦截器栈中,可以包含多个拦截器的引用拦截器栈的引用和拦截器一致 --> <interceptor-stack name="myStack"> <!-- 引用拦截器 --> <interceptor-ref name="time"/> <!-- 引用默认的拦截器栈 ;引用拦截器和引用拦截器栈的方式一样--> <interceptor-ref name="defaultStack"/> </interceptor-stack> |
<action name="hello" class="cn.sxt.action.HelloAction"> <result>/index.jsp</result> <interceptor-ref name="myStack"/> </action> |
4)当自定义拦截器栈在这个包下的所有 action 都使用的时,可以定义为默认的拦截器栈,或默认的拦截器。
<!-- 定义默认的拦截器/栈 --> <default-interceptor-ref name="myStack"/> <action name="hello" class="cn.sxt.action.HelloAction"> <result>/index.jsp</result> </action> |
案例:对于登录权限的控制
登录验证拦截器
public class LoginInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation actionInvocation) throws Exception {
//判断是否是login.action,如果是logina.action则直接执行下一个执行器
//如果不是,则通过session判断是否登陆,如果已经登陆,则执行下一个执行器
//如果没有登陆则返回登陆页面
String actionName = actionInvocation.getProxy().getActionName();
if("login".equals(actionName)){
return actionInvocation.invoke();
}
Object obj = actionInvocation.getInvocationContext().getSession().get("user");
if(obj == null){
return Action.LOGIN;
}
return actionInvocation.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="hello" extends="struts-default" >
<!-- 配置拦截器-->
<interceptors>
<interceptor name="LoginInterceptor" class="com.xijian.interceptor.LoginInterceptor"/>
<!-- 一个拦截器栈中,可以包含多个拦截器的引用,在action中引入时和引入普通拦截器一扬-->
<interceptor-stack name="myStack">
<!-- 引用拦截器-->
<interceptor-ref name="LoginInterceptor"></interceptor-ref>
<interceptor-ref name="timer"></interceptor-ref>
<!-- 引用默认的拦截器栈-->
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 定义默认的拦截器/栈,适用于所有action-->
<default-interceptor-ref name="myStack"/>
<!-- 定义全局结果集-->
<global-results>
<result name="login">/login.jsp</result>
</global-results>
<action name="hello" class="com.xijian.controller.HelloAction" method="hello">
<result name="success">/index.jsp</result>
</action>
<action name="login" class="com.xijian.controller.LoginAction" method="login">
<result>/success.jsp</result>
</action>
</package>
</struts>
login.jsp页面
<%--
Created by IntelliJ IDEA.
User: 席剑
Date: 2019/10/15
Time: 11:51
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="login.action" method="post">
<input type="text" name="username">
<input type="text" name="password">
<input type="submit" value="login">
</form>
</body>
</html>
LoginAction
/**
* @author XiJian
* @date 2019/10/15 - 11:48
*/
public class LoginAction implements ModelDriven<User> {
public User user = new User();
public User getModel(){
return this.user;
}
public String login(){
System.out.println(user.getUsername()+"-----"+user.getPassword());
if("zhangsan".equals(user.getUsername()) && "123".equals(user.getPassword())) {
ActionContext.getContext().getSession().put("user",user.getUsername());
return Action.SUCCESS;
}
return Action.LOGIN;
}
}
9.方法拦截器:方法拦截器时比 Action 拦截器更加细粒度的控制,主体实现和 Action 拦截器一致。但是方法拦截器时继承 MethodFilterInterceptor 类,重写 doIntercept()方法。引用方法拦截器配置会发生改变:
<interceptor-ref name="methodInterceptor">
<!-- 配置被拦截的方法 -->
<param name="includeMethods">list,add</param>
<!-- 配置不被拦截的方法 -->
<param name="excludeMethods">login</param>
</interceptor-ref>
二、表单重复提交问题的解决
即当前页面有form表单元素,用户在点击提交后重复刷新页面,可导致后台对应的Action接到多条重复数据,这显然是非常不合理的,在Structs2中框架提供给我们一种解决方案,就是引入 token 拦截器。
在jsp页面中也需要引入Structs2标签库,在form表单中引入一个<s:token>标签
当表单重复提交时,会返回invalid.token,可以根据这个返回结果跳转到对应错误页面。
原理是:在第一次提交表单时,会在session中存入一个token值(令牌),这个值也会随着响应写入到jsp页面中,当再次提交表单时,请求中会有此token码,然后发起请求时去session中验证,如果session中存在,即允许表单提交,如果session中此码不存在,证明是无效的提交。
当每一次提交成功后都会将session中的token码删除。