Struts2之拦截器

摘要: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"/>
<!--定义一个拦截器栈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,且该内置拦截器栈必不可少。

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"/>
...
<!-- 定义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接口

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("我是拦截器的拦截方法");
//调用invoke()方法,代表着放行执行下一个拦截器,如果没有拦截器了,那么就执行Action的业务代码
//可看成是过滤器的doFilter()方法
actionInvocation.invoke();
return null;
}

AbstractInterceptor类实现了Interceptor接口的所有方法,只需继承AbstractInterceptor类,实现interceptor()方法就可以创建自定义拦截器。

使用拦截器实现权限控制

自定义一个拦截器需要三步:

  1. 自定义一个实现Interceptor接口(或者继承自AbstractInterceptor)的类。
  2. 在strutx.xml中注册上一步中定义的拦截器。
  3. 在需要使用的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 = 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

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();// 得到ActionContext.
Object user = actionContext.getSession().get("user");//获取user对象
if (user != null) {
return invocation.invoke(); // 继续向下执行
} else {
actionContext.put("msg", "您还未登录,请先登录");
return Action.LOGIN; //如果用户不存在,返回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>
<!-- 关于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


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!