目录
- 拦截器简介
- 拦截器配置
- 内建拦截器
- 自定义拦截器
- 使用拦截器实现权限控制
- 拦截器工作原理
拦截器简介
拦截器(Interceptor)是Struts2的核心组成部分,它可以动态拦截Action调用的对象,类似于Servlet中的过滤器。 Struts2的拦截器是AOP(Aspect-Object-Programming,面向切面编程)的一种实现策略,是可插拔的。它可以任意地组合Action提供的附加功能,而不需要修改Action的代码,开发者只需要提供拦截器的实现类,并将其配置在struts.xml中即可。
每个拦截器可以完成单个功能。不同功能的拦截器按照一定的顺序排列在一起形成的链,就是拦截器链。拦截器链组成的集合就是拦截器栈。
拦截器配置
1.拦截器
要想拦截器起作用,首先要在struts.xml文件中进行配置
<interceptor name="interceptorName" class="interceptorClass"><param name="paramName">paramValue</param>
</interceptor>
name属性用来指定拦截器的名称,class属性用于指定拦截器的实现类。如果在定义拦截器时需要传入参数,这时需要使用<param>
标签,其中name属性用来指定参数的名称,paramValue表示参数的值。
2.拦截栈
在实际开发中,经常需要在Action执行前同时执行多个拦截动作,如用户登录检查、登录日志记录以及权限检查等,这时,可以把多个拦截器组成一个拦截器栈。
在使用时,可以将栈内的多个拦截器当成个整体来引用。 当拦截器栈被附加到一个 Action上时,在执行Action之前必须先执行拦截器栈中的每一一 个拦截器。
<interceptors><interceptor-stack name="interceptorStackName"><interceptor-ref name="interceptorName" />...</interceptor-stack>
</interceptors>
interceptorStackName值代表配置的拦截器栈的名称,interceptorName值代表拦截器的名称。
除此之外,一个拦截器栈也可以包含另外一个拦截器栈:
<struts><package name="xxx" extends="struts-default" ><!--声明拦截器--><interceptors><interceptor name="interceptor1" class="interceptorClass"/><interceptor name="interceptor2" class="interceptorClass"/><!--定义一个拦截器栈myStack,该拦截器栈中包含两个拦截器和一个拦截器栈--><interceptor-stack name="myStack"><interceptor-ref name="defaultStack" /><interceptor-ref name="interceptor1" /><interceptor-ref name="interceptor2" /></interceptor-stack></interceptors>
</struts>
在定义的拦截栈myStack中,除了引用了两个自定义的拦截器interceptor1和interceptor2外,还引用了一个内置拦截器栈defaultStack,且该内置拦截器栈必不可少。
<struts><package name="xxx" extends="struts-default" ><interceptors><!--配置用户自定义的拦截器--><interceptor name="MyInterceptor" class="TestAction"/><!--自定义拦截器栈,我们配置了自定义的拦截器,默认的拦截器栈就不会被执行,因此,想要使用默认的拦截器功能,就要配置进来--><interceptor-stack name="mystack"><!--引用默认的拦截器栈,一定要放在第一行--><interceptor-ref name="defalutStack"/><!--引用自定义的拦截器--><interceptor-ref name="MyInterceptor"/></interceptor-stack></interceptors><!--上面配置了拦截器栈,但是没有被执行...下面配置执行拦截器--><default-interceptor-ref name="mystack"/><action name="TestAction" class="TestAction" method="execute"><result name="success">/index.jsp</result></action></package>
</struts>
内建拦截器
拦截器都继承AbstractInterceptor
Struts2 框架中内置了许多拦截器,这些拦截器以 name-class 对的形式配置在 struts-default.xml 文件中,其中,name 是拦截器的名称,也就是引用的名字;class 指定了该拦截器所对应的实现。
只要自定义的包继承了 Struts2 的 struts-default 包,就可以使用包中定义的内建拦截器,否则需要自行定义拦截器。
内建拦截器介绍:
名称 | 说明 |
---|---|
alias | 在不同请求之间将请求参数在不同名称间转换,请求内容不变 |
autowiring | 用于实现 Action 的自动装配 |
chain | 让前一个 Action 的属性可以被后一个 Action 访问,现在和 chain 类型的 result() 结合使用 |
conversionError | 将错误从 ActionContext 中添加到 Action 的属性字段中 |
cookies | 使用配置的 Name 和 Value 指定 Cookies |
cookieProvider | 该类是一个 Cookie 工具,方便开发者向客户端写 Cookie |
clearSession | 用于清除一个 HttpSession 实例 |
createSession | 自动创建 HttpSession,用于为需要使用 |
HttpSession | 的拦截器服务 |
debugging | 提供不同的调试用的页面展现内部的数据状况 |
execAndWait | 在后台执行 Action,同时将用户带到一个中间的等待页面 |
exception | 将异常定位到一个画面 |
fileUpload | 提供文件上传功能 |
il8n | 记录用户选择的 locale |
logger | 输出 Action 的名称 |
model-driven | 如果一个类实现了 Model Driven,将 get Model 得到的结果放在 Value Slack 中 |
scoped-model-driven | 如果一个 Action 实现了 |
params | 将请求中的参数设置到 Action 中 |
actionMappingParams | 用于负责在 Action 配置中传递参数 |
prepare | 如果 Action 实现了 Preparable,则该拦截器调用 Action 类的 prepare 方法 |
staticParams | 将 struts.xml 文件中 标签的参数内容设置到对应的 Action 中 |
scope | 将 Action 状态存入 session 和 application 范围 |
servletConfig | 提供访问 HttpServletRequest 和 HttpServletResponse 方法,以 Map 方式访问 |
timer | 输岀 Action 执行的时间 |
token | 通过 Token 避免双击 |
tokenSession | 和 Token Interceptor 一样,不过双击时把请求的数据存储在 Session 中 |
validation | 使用 action-validation.xml 文件中定义的内容校验提交的数据 |
workflow | 调用 Action 的 validate 方法,一旦有错谋返回,则重新定位到 INPUT 画面 |
store | 存储或者访问实现 ValidalionAware 接口的 Action 类出现的消息、错误和字段错误等 |
checkbox | 添加了 checkbox 自动处理代码,将没有选中的 checkbox 的内容设定为 false,而 html 在默认情况下不提交没有选中的 checkbox |
datetime | 日期拦截器 |
profiling | 通过参数激活 profile |
roles | 确定用户是否具有 JAAS 指定的 Role,否则不予执行 |
annotationWorkflow | 利用注解代替 XML 配置 |
multiselect | 检测是否有像 标签一样被选中的多个值,然后添加一个空参数 |
deprecation | 当日志级别设置为调试模式(debug)并且没有特殊参数时,在 devMode |
在 struts-core-2.3.24.jar 包中的根目录下找到 struts-default.xml 文件,打开后找到 元素下的内建拦截器和拦截器栈,其部分代码如下所示:
<package name="struts-default" abstract="true">...<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"/>...<!-- 定义Basic stack拦截器栈 --><interceptor-stack name="basicStack"><!--引用系统定义的exception拦截器--><interceptor-ref name="exception"/>...</interceptor-stack>...<!-- 定义Sample model -driven stack --><interceptor-stack name="modelDrivenStack"><!--引用系统定义的modelDriven拦截器--><interceptor-ref name="modelDriven"/><!--引用系统定义的basicStack拦截器栈--><interceptor-ref name="basicStack"/></interceptor-stack>...<!--定义defaultStack拦截器栈--><interceptor-stack name="defaultStack"><interceptor-ref name="exception"/><interceptor-ref name="alias"/><interceptor-ref name="il8n"/>...<interceptor-ref name="validation"><param name="excludeMethods">input,back,cancel,browse</param></interceptor-ref>...</interceptor-stack></interceptors><!--将defaulrStack拦截器栈配置为系统默认拦截器栈--><default-interceptor-ref name="defaultStack"/><!--默认action类是ActionSupport--><default-class-ref class="com.opensymphony.xwork2.ActionSupport" />
</package>
在上述内建拦截器的配置代码中,defaultStack 拦截器组合了多个拦截器,这些拦截器可以满足大部分 Web 应用程序的需求。使用时,只要在 struts.xml 定义包的过程中继承 struts-default 包,那么 defaultStack 拦截器栈就是默认拦截器的引用。
自定义拦截器
在实际的项目开发中,Struts2的内置拦截器可以完成大部分的拦截任务,但是,一些与系统逻辑相关的通用功能(如权限的控制、用户登录控制等),则需要通过自定义拦截器来实现,自定义拦截器其实就是开发自己的拦截器类。
自定义的拦截器类,需要实现import com.opensymphony.xwork2.interceptor.Interceptor接口
public interface Interceptor extends Serializable {void destroy();void init();String intercept(ActionInvocation invocation) throws Exception;
}
该接口提供了以下三个方法:
-
void init():该方法在拦截器被创建后会立即被调用,它在拦截器的生命周期内只被调用一-次。可以在该方法中对相关资源进行必要的初始化。
-
void destroy():该方法与init()方法相对应,在拦截器实例被销毁之前,将调用该方法来释放与拦截器相关的资源。它在拦截器的生命周期内也只被调用一次。
-
String intercept( ActionInvocation invocation) throws Exception: 该方法是拦截器的核心方法,用来添加真正执行拦截工作的代码,实现具体的拦截操作。它返回一个字符串作为逻辑视图,系统根据返回的字符串跳转到对应的视图资源。每拦截一个动作请求,该方法就会被调用一次。该方法的ActionInvocation参数包含了被拦截的Action的引用,可以通过该参数的invoke()方法,将控制权转给下一个拦截器或者转给Action的execute()方法。
如果需要自定义拦截器,只需要实现Interceptor接口的三个方法即可。然而在实际开发过程中,除了实现Interceptor接口可以自定义拦截器外,更常用的一种方式是继承抽象拦截器类AbstractIntercepter。
该类实现了Interceptor 接口,并且提供了init()方法 和destroy()方法的空实现。使用时,可以直接继承该抽象类,而不用实现那些不必要的方法”拦截器类AbstractInterceptor中定义的方法
如下所示:
public abstract class AbstractInterceptor implements Interceptor {public void init() {System.out.println("我是拦截器的初始化方法");}public void destroy() {System.out.println("我是拦截器的销毁方法");}public abstract String intercept(ActionInvocation invocation) throws Exception{System.out.println("我是拦截器的拦截方法");//调用invoke()方法,代表着放行执行下一个拦截器,如果没有拦截器了,那么就执行Action的业务代码//可看成是过滤器的doFilter()方法actionInvocation.invoke();return null;
}
AbstractInterceptor类实现了Interceptor接口的所有方法,只需继承AbstractInterceptor类,实现interceptor()方法就可以创建自定义拦截器。
使用拦截器实现权限控制
自定义一个拦截器需要三步:
- 自定义一个实现Interceptor接口(或者继承自AbstractInterceptor)的类。
- 在strutx.xml中注册上一步中定义的拦截器。
- 在需要使用的Action中引用上述定义的拦截器,为了方便也可将拦截器定义为默认的拦截器,这样在不加特殊声明的情况下所有的Action都被这个拦截器拦截。
源代码如下:
User.java
package cn.itcast.domain;
public class User {private String username; //用户名private String password; //密码public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return "User [username=" + username + ", password=" + password + "]";}}
LoginAction.java
package cn.itcast.action;
import cn.itcast.domain.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
public class LoginAction extends ActionSupport implements ModelDriven<User> {private static final long serialVersionUID = 1L;private User user = new User();@Overridepublic User getModel() {return user;}@Overridepublic String execute() throws Exception {//获取ActionContextActionContext actionContext = ActionContext.getContext();System.out.println(user);if ("tom".equals(user.getUsername())&& "123".equals(user.getPassword())) {// 将用户存储在session中actionContext.getSession().put("user", user);return SUCCESS;} else {actionContext.put("msg", "用户名或密码不正确");return INPUT;}}
}
BookAction.java
package cn.itcast.action;
import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class BookAction extends ActionSupport {public String add() {System.out.println("book add");return SUCCESS;}public String del() {System.out.println("book del");return SUCCESS;}public String update() {System.out.println("book update");return SUCCESS;}public String find() {System.out.println("book find");return SUCCESS;}
}
PrivilegeInterceptor.java
package cn.itcast.interceptor;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class PrivilegeInterceptor extends AbstractInterceptor {private static final long serialVersionUID = 1L;@Overridepublic String intercept(ActionInvocation invocation) throws Exception {ActionContext actionContext = invocation.getInvocationContext();// 得到ActionContext.Object user = actionContext.getSession().get("user");//获取user对象if (user != null) {return invocation.invoke(); // 继续向下执行} else {actionContext.put("msg", "您还未登录,请先登录");return Action.LOGIN; //如果用户不存在,返回login值}}
}
login.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>登录</title>
</head>
<body>
<center>
${requestScope.msg}<br><form action="login" method="post"><table><tr><td><label style="text-align: right;">用户名:</label></td><td><input type="text" name="username"></td></tr><tr><td><label style="text-align: right;">密码:</label></td><td><input type="password" name="password"></td></tr><tr><td align="right" colspan="2"><input type="submit" value="登录"></td></tr></table></form>
</center>
</body>
</html>
main.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head> <title>main.jsp</title></head><body><a href="/chapter03/book_del">book del</a><br><a href="/chapter03/book_add">book add</a><br><a href="/chapter03/book_update">book update</a><br><a href="/chapter03/book_find">book find</a><br></body>
</html>
success.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>成功页面</title>
</head>
<body>用户${user.username}操作成功
</body>
</html>
struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN""http://struts.apache.org/dtds/struts-2.3.dtd">
<struts><package name="struts2" namespace="/" extends="struts-default"><!-- 声明拦截器 --><interceptors><interceptor name="privilege" class="cn.itcast.interceptor.PrivilegeInterceptor"/><interceptor-stack name="myStack"><interceptor-ref name="defaultStack" /><interceptor-ref name="privilege"></interceptor-ref></interceptor-stack></interceptors><!-- 用户登录操作 --><action name="login" class="cn.itcast.action.LoginAction"><result>/main.jsp</result><result name="input">/login.jsp</result></action><!-- 关于book操作 --><action name="book_*" class="cn.itcast.action.BookAction"method="{1}"><result>/success.jsp</result><result name="login">/login.jsp</result><interceptor-ref name="myStack" /></action></package>
</struts>
发布项目,访问main.jsp
点击连接,由于没有登陆,所以没有权限,页面跳转到登录页面要求用户登录。
登陆成功后点击链接,跳转成功页面,提示用户操作成功。
对比可知拦截器成功执行
以上案例中,创建了一个方法过滤器PrivilegeInterceptor,在struts.xml中配置了该拦截器,如果用户没有登陆,将无法对页面链接进行相应操作,只有登陆后的用户才有权限操作页面相应功能。
拦截器工作原理
拦截器的执行顺序
invocation.invoke()方法是拦截器框架的实现核心,通过确定invocation.invoke()方法执行位置,来实现Action执行前后处理操作,在invocation.invoke()方法之前的代码将依据配置中拦截器顺序依次执行,直到走完拦截器后再执行invocation.invoke()方法调用Action,然后再依据配置中拦截器顺序反向执行invocation.invoke()方法后的代码,直到走完拦截器
拦截器的工作原理
如图所示
每一个Action请求都包装在一系列的拦截器的内部。拦截器可以在Action执行直线做相似的操作也可以在Action执行直后做回收操作。
每一个Action既可以将操作转交给下面的拦截器,Action也可以直接退出操作返回客户既定的画面。
https://www.cnblogs.com/yw-ah/p/5761235.html
https://www.cnblogs.com/wkrbky/p/5894315.html
https://blog.csdn.net/weixin_42529699/article/details/81354355
https://www.imooc.com/learn/450