Struts 2 拦截器
拦截器在概念上与 servlet 过滤器或 JDK 代理类相同。 拦截器允许与动作和框架分开实现横切功能。 我们可以使用拦截器实现以下目标
- 在调用动作之前提供预处理逻辑。
- 在调用动作后提供后处理逻辑。
- 捕获异常以便可以执行替代处理。
Struts2 框架中提供的许多特性都是使用拦截器实现的;
示例包括异常处理、文件上传、生命周期回调等。事实上,由于 Struts2 的大部分功能都强调在拦截器上,因此不太可能为每个操作分配 7 或 8 个拦截器。
Struts2 框架拦截器
Struts 2 框架提供了一个很好的开箱即用拦截器列表,这些拦截器预先配置好并可以使用。 下面列出了几个重要的拦截器
序号 | 拦截器 | 描述 |
---|---|---|
1 | alias | 允许参数在请求中具有不同的名称别名。 |
2 | checkbox | 通过为未选中的复选框添加参数值 false 来帮助管理复选框。 |
3 | conversionError | 将来自将字符串转换为参数类型的错误信息放入Action的 errors 字段中。 |
4 | createSession | 如果一个 HTTP 会话不存在,则自动创建一个。 |
5 | debugging | 为开发人员提供几个不同的调试屏幕。 |
6 | execAndWait | 当操作在后台执行时,将用户定向到中间等待页面。 |
7 | exception | 将从动作抛出的异常映射到结果,允许通过重定向自动处理异常。 |
8 | fileUpload | 便于文件上传。 |
9 | i18n | 在用户会话期间跟踪选定的语言环境。 |
10 | logger | 通过输出正在执行的操作的名称来提供简单的日志记录。 |
11 | params | 设置动作的请求参数。 |
12 | prepare | 这通常用于进行预处理工作,例如设置数据库连接。 |
13 | profile | 允许为操作记录简单的分析信息。 |
14 | scope | 在会话或应用程序范围内存储和检索操作的状态。 |
15 | ServletConfig | 为操作提供访问各种基于 servlet 的信息的权限。 |
16 | timer | 以执行操作所需时间的形式提供简单的分析信息。 |
17 | token | 检查有效令牌的操作以防止重复提交表单。 |
18 | validation | 为操作提供验证支持 |
请查看 Struts 2 文档以获取有关上述拦截器的完整详细信息。 但我们将展示如何在 Struts 应用程序中使用拦截器。
如何使用拦截器
让我们看看如何在“Hello World”程序中使用已经存在的拦截器。 我们将使用计时器拦截器,其目的是测量执行操作方法所用的时间。 同时,我正在使用 params
拦截器,其目的是将请求参数发送到 Action。 我们可以在不使用此拦截器的情况下尝试我们的示例,这时会发现 name 属性未设置,因为参数无法访问 Action。
我们将保留在示例章节中创建的 HelloWorldAction.java、web.xml、HelloWorld.jsp 和 index.jsp 文件,但让我们修改 struts.xml 文件来添加拦截器,如下所示
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name = "struts.devMode" value = "true" />
<package name = "helloworld" extends = "struts-default">
<action name = "hello"
class = "com.jiyik.struts2.action.HelloWorldAction"
method = "execute">
<interceptor-ref name = "params"/>
<interceptor-ref name = "timer" />
<result name = "success">/HelloWorld.jsp</result>
<result name = "error">/AccessDenied.jsp</result>
</action>
</package>
</struts>
启动服务,在浏览器中进行访问
现在在给定的文本框中输入任何单词,然后单击 Say Hello 按钮来执行定义的操作。 现在,如果检查生成的日志,我们会发现以下文本
21:45:17,953 INFO TimerInterceptor:205 - Executed action [//hello!execute] took 112 ms.
创建自定义拦截器
在应用程序中使用自定义拦截器是提供横切应用程序功能的一种优雅方式。 创建自定义拦截器很容易; 需要实现Interceptor接口
public interface Interceptor extends Serializable {
void destroy();
void init();
String intercept(ActionInvocation invocation)
throws Exception;
}
顾名思义,init()
方法提供了一种初始化拦截器的方法,而 destroy()
方法用于拦截器清理的工作。 与 Action 不同,拦截器在请求之间被重用并且需要是线程安全的,尤其是 intercept()
方法。
ActionInvocation
对象提供对运行时环境的访问。 它允许访问 Action 本身和调用该操作并确定该操作是否已被调用的方法。
如果我们不需要初始化或清理代码,可以继承 AbstractInterceptor
类。 它提供了 init()
和 destroy()
方法的默认无操作实现。
创建拦截器类
让我们在 Java Resources > src 文件夹中创建以下 MyInterceptor.java 代码
package com.jiyik.struts2.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class MyInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
String output = "Pre-Processing";
System.out.println(output);
String result = invocation.invoke();
output = "Post-Processing";
System.out.println(output);
return result;
}
}
如大家所见,实际操作将通过 invocation.invoke()
调用使用拦截器执行。 因此,我们可以根据要求进行一些预处理和一些后处理。
框架本身通过对 ActionInvocation
对象的 invoke()
进行第一次调用来启动该过程。 每次调用 invoke()
时,ActionInvocation
都会查询其状态并执行接下来出现的任何拦截器。 当所有配置的拦截器都被调用时,invoke()
方法将导致动作本身被执行。
下图通过请求流展示了相同的概念
创建 Action 类
让我们在 Java Resources > src 下创建一个 java 文件 HelloWorldAction.java,包名为 com.jiyik.struts2.action
,内容如下。
package com.jiyik.struts2.action;
import com.opensymphony.xwork2.ActionSupport;
public class HelloWorldAction extends ActionSupport {
private String name;
@Override
public String execute() throws Exception {
System.out.println("Inside action....");
return SUCCESS;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
这是我们在前面的例子中看到的同一个类。 我们有用于“name”属性的标准 getter
和 setter
方法以及返回字符串“success”的执行方法。
创建视图
让我们在 IDEA 项目的 Web 文件夹中创建以下 jsp 文件 HelloWorld.jsp。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hello World</title>
</head>
<body>
Hello World, ${name}
</body>
</html>
我们还需要在 Web 文件夹中创建 index.jsp。 该文件将用作初始操作 URL,用户可以单击该 URL 以告知 Struts 2 框架调用 HelloWorldAction
类的已定义方法并呈现 HelloWorld.jsp 视图。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hello World</title>
</head>
<body>
<h1>Hello World From Struts2</h1>
<form action = "hello">
<label for = "name">请输入您的姓名</label><br/>
<input type = "text" name = "name"/>
<input type = "submit" value = "Say Hello"/>
</form>
</body>
</html>
上述视图文件中定义的 hello 动作将使用 struts.xml 文件映射到 HelloWorldAction
类及其执行方法。
配置文件
现在,我们需要注册我们的拦截器,然后调用它,就像我们在前面的示例中调用默认拦截器一样。 为了注册一个新定义的拦截器,<interceptors>...</interceptors>
标签直接放在 <package>
标签 insstruts.xml 文件下。 对于默认拦截器,我们可以跳过这一步,就像我们在前面的示例中所做的那样。 但是这里让我们注册并使用如下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name = "struts.devMode" value = "true" />
<package name = "helloworld" extends = "struts-default">
<interceptors>
<interceptor name="myinterceptor" class="com.jiyik.struts2.interceptor.MyInterceptor"></interceptor>
</interceptors>
<action name = "hello"
class = "com.jiyik.struts2.action.HelloWorldAction"
method = "execute">
<interceptor-ref name = "params"/>
<interceptor-ref name = "myinterceptor" />
<result name = "success">/HelloWorld.jsp</result>
<result name = "error">/AccessDenied.jsp</result>
</action>
</package>
</struts>
需要注意的是,我们可以在 <package>
标签内注册多个拦截器,同时我们可以在 <action>
标签内调用多个拦截器。 我们可以使用不同的操作调用相同的拦截器。
web.xml 文件需要在 Web 下的WEB-INF文件夹下创建如下文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
我们直接使用 IDEA 运行应用程序。在浏览器中输入地址 http://localhost:8080
。
现在在给定的文本框中输入任何单词,然后单击 Say Hello 按钮以执行定义的操作。 现在,如果我们检查生成的日志,内容将如下图所示
堆叠多个拦截器
可以想象,必须为每个操作配置多个拦截器很快就会变得非常难以管理。 出于这个原因,拦截器使用拦截器堆栈进行管理。 这是一个示例,直接来自 strutsdefault.xml
文件
<interceptor-stack name = "basicStack">
<interceptor-ref name = "exception"/>
<interceptor-ref name = "servlet-config"/>
<interceptor-ref name = "prepare"/>
<interceptor-ref name = "checkbox"/>
<interceptor-ref name = "params"/>
<interceptor-ref name = "conversionError"/>
</interceptor-stack>
上面的形式称为 basicStack,可以在配置中使用,如下所示。 此配置节点位于 <package .../>
节点下。 每个 <interceptor-ref .../>
标签引用一个拦截器或一个在当前拦截器堆栈之前配置的拦截器堆栈。 因此,在配置初始拦截器和拦截器堆栈时,确保名称在所有拦截器和拦截器堆栈配置中是唯一的非常重要。
我们已经看到了如何将拦截器应用于 Action ,应用拦截器堆栈也不例外。 事实上,我们使用完全相同的标签
<action name = "hello" class = "com.jiyik.struts2.action">
<interceptor-ref name = "basicStack"/>
<result>view.jsp</result>
</action>
上面的“basicStack”注册将注册所有六个带有 hello 动作的拦截器。 需要注意的是,拦截器是按照它们被配置的顺序执行的。 例如,在上述情况下,将首先执行 exception,其次是 servlet-config,依此类推。