摘要:Struts2框架学习笔记(三)
拦截器简介
拦截器(Interceptor)是Struts2的核心组成部分,它可以动态拦截Action调用的对象,类似于Servlet中的过滤器。 Struts2的拦截器是AOP(Aspect-Object-Programming,面向切面编程)的一种实现策略,是可插拔的。它可以任意地组合Action提供的附加功能,而不需要修改Action的代码,开发者只需要提供拦截器的实现类,并将其配置在struts.xml中即可。
每个拦截器可以完成单个功能。不同功能的拦截器按照一定的顺序排列在一起形成的链,就是拦截器链。拦截器链组成的集合就是拦截器栈。
拦截器配置
1.拦截器
要想拦截器起作用,首先要在struts.xml文件中进行配置
1 2 3
| <interceptor name="interceptorName" class="interceptorClass"> <param name="paramName">paramValue</param> </interceptor>
|
name属性用来指定拦截器的名称,class属性用于指定拦截器的实现类。如果在定义拦截器时需要传入参数,这时需要使用<param>
标签,其中name属性用来指定参数的名称,paramValue表示参数的值。
2.拦截栈
在实际开发中,经常需要在Action执行前同时执行多个拦截动作,如用户登录检查、登录日志记录以及权限检查等,这时,可以把多个拦截器组成一个拦截器栈。
在使用时,可以将栈内的多个拦截器当成个整体来引用。 当拦截器栈被附加到一个 Action上时,在执行Action之前必须先执行拦截器栈中的每一一 个拦截器。
1 2 3 4 5 6
| <interceptors> <interceptor-stack name="interceptorStackName"> <interceptor-ref name="interceptorName" /> ... </interceptor-stack> </interceptors>
|
interceptorStackName值代表配置的拦截器栈的名称,interceptorName值代表拦截器的名称。
除此之外,一个拦截器栈也可以包含另外一个拦截器栈:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <struts> <package name="xxx" extends="struts-default" > <interceptors> <interceptor name="interceptor1" class="interceptorClass"/> <interceptor name="interceptor2" class="interceptorClass"/> <interceptor-stack name="myStack"> <interceptor-ref name="defaultStack" /> <interceptor-ref name="interceptor1" /> <interceptor-ref name="interceptor2" /> </interceptor-stack> </interceptors> </struts>
|
在定义的拦截栈myStack中,除了引用了两个自定义的拦截器interceptor1和interceptor2外,还引用了一个内置拦截器栈defaultStack,且该内置拦截器栈必不可少。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <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 文件,打开后找到 元素下的内建拦截器和拦截器栈,其部分代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <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"/> ... <interceptor-stack name="basicStack"> <interceptor-ref name="exception"/> ... </interceptor-stack> ... <interceptor-stack name="modelDrivenStack"> <interceptor-ref name="modelDriven"/> <interceptor-ref name="basicStack"/> </interceptor-stack> ... <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> <default-interceptor-ref name="defaultStack"/> <default-class-ref class="com.opensymphony.xwork2.ActionSupport" /> </package>
|
在上述内建拦截器的配置代码中,defaultStack 拦截器组合了多个拦截器,这些拦截器可以满足大部分 Web 应用程序的需求。使用时,只要在 struts.xml 定义包的过程中继承 struts-default 包,那么 defaultStack 拦截器栈就是默认拦截器的引用。
自定义拦截器
在实际的项目开发中,Struts2的内置拦截器可以完成大部分的拦截任务,但是,一些与系统逻辑相关的通用功能(如权限的控制、用户登录控制等),则需要通过自定义拦截器来实现,自定义拦截器其实就是开发自己的拦截器类。
自定义的拦截器类,需要实现import com.opensymphony.xwork2.interceptor.Interceptor接口
1 2 3 4 5 6 7 8 9
| 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中定义的方法
如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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("我是拦截器的拦截方法"); actionInvocation.invoke(); return null; }
|
AbstractInterceptor类实现了Interceptor接口的所有方法,只需继承AbstractInterceptor类,实现interceptor()方法就可以创建自定义拦截器。
使用拦截器实现权限控制
自定义一个拦截器需要三步:
- 自定义一个实现Interceptor接口(或者继承自AbstractInterceptor)的类。
- 在strutx.xml中注册上一步中定义的拦截器。
- 在需要使用的Action中引用上述定义的拦截器,为了方便也可将拦截器定义为默认的拦截器,这样在不加特殊声明的情况下所有的Action都被这个拦截器拦截。
源代码如下:
User.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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; } @Override public String toString() { return "User [username=" + username + ", password=" + password + "]"; } }
|
LoginAction.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| 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(); @Override public User getModel() { return user; } @Override public String execute() throws Exception { ActionContext actionContext = ActionContext.getContext(); System.out.println(user); if ("tom".equals(user.getUsername()) && "123".equals(user.getPassword())) { actionContext.getSession().put("user", user); return SUCCESS; } else { actionContext.put("msg", "用户名或密码不正确"); return INPUT; } } }
|
BookAction.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 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; @Override public String intercept(ActionInvocation invocation) throws Exception { ActionContext actionContext = invocation.getInvocationContext(); Object user = actionContext.getSession().get("user"); if (user != null) { return invocation.invoke(); } else { actionContext.put("msg", "您还未登录,请先登录"); return Action.LOGIN; } } }
|
login.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <%@ 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
1 2 3 4 5 6 7 8 9 10 11 12 13
| <%@ 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
1 2 3 4 5 6 7 8 9 10
| <%@ 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <?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> <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