Struts 2 - 拦截器
拦截器在概念上与 servlet 过滤器或 JDK 代理类相同。拦截器允许独立于操作和框架实现横切功能。您可以使用拦截器 − 实现以下功能
在调用操作之前提供预处理逻辑。
在调用操作之后提供后处理逻辑。
捕获异常以便执行替代处理。
Struts2 框架中提供的许多功能都是使用拦截器实现的;
示例包括异常处理、文件上传、生命周期回调等。事实上,由于 Struts2 强调其大部分功能都依赖于拦截器,因此不太可能为每个操作分配 7 或 8 个拦截器。
Struts2 框架拦截器
Struts 2 框架提供了一个很好的开箱即用拦截器列表,这些拦截器已预先配置并可立即使用。下面列出了一些重要的拦截器 −
Sr.No | 拦截器 &描述 |
---|---|
1 | alias 允许参数在不同的请求中使用不同的名称别名。 |
2 | checkbox 通过为未选中的复选框添加参数值 false 来协助管理复选框。 |
3 | conversionError 将字符串转换为参数类型的错误信息放入操作的字段错误中。 |
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"程序中使用现有的拦截器。我们将使用 timer 拦截器,其目的是测量执行操作方法所需的时间。同时,我使用 params 拦截器,其目的是将请求参数发送到操作。您可以尝试不使用这个拦截器的示例,您会发现 name 属性未设置,因为参数无法到达操作。
我们将保留 HelloWorldAction.java、web.xml、HelloWorld.jsp 和 index.jsp 文件,因为它们已在 Examples 一章中创建,但让我们修改 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.tutorialspoint.struts2.HelloWorldAction"
method = "execute">
<interceptor-ref name = "params"/>
<interceptor-ref name = "timer" />
<result name = "success">/HelloWorld.jsp</result>
</action>
</package>
</struts>
右键单击项目名称,然后单击 Export > WAR File 以创建 War 文件。然后将此 WAR 部署到 Tomcat 的 webapps 目录中。最后,启动 Tomcat 服务器并尝试访问 URL http://localhost:8080/HelloWorldStruts2/index.jsp。这将产生以下屏幕 −

现在在给定的文本框中输入任何单词,然后单击 Say Hello 按钮以执行定义的操作。现在,如果您检查生成的日志,您将找到以下文本 −
INFO: Server startup in 3539 ms
27/08/2011 8:40:53 PM
com.opensymphony.xwork2.util.logging.commons.CommonsLogger info
INFO: Executed action [//hello!execute] took 109 ms.
此处的底线是由于 timer 拦截器生成的,它表明该操作总共花费了 109 毫秒才能执行。
创建自定义拦截器
在应用程序中使用自定义拦截器是一种提供横切应用程序功能的优雅方式。创建自定义拦截器很容易;需要扩展的接口是以下 Interceptor 接口 −
public interface Interceptor extends Serializable {
void destroy();
void init();
String intercept(ActionInvocation invocation)
throws Exception;
}
顾名思义,init() 方法提供了一种初始化拦截器的方法,而 destroy() 方法提供了一种拦截器清理工具。与操作不同,拦截器可在请求之间重复使用,并且需要线程安全,尤其是 Intercept() 方法。
ActionInvocation 对象提供对运行时环境的访问。它允许访问操作本身以及调用操作的方法并确定操作是否已被调用。
如果您不需要初始化或清理代码,可以扩展 AbstractInterceptor 类。这提供了 init() 和 destroy() 方法的默认无操作实现。
创建拦截器类
让我们在 Java Resources > src 文件夹中创建以下 MyInterceptor.java −
package com.tutorialspoint.struts2;
import java.util.*;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class MyInterceptor extends AbstractInterceptor {
public String intercept(ActionInvocation invocation)throws Exception {
/* let us do some pre-processing */
String output = "Pre-Processing";
System.out.println(output);
/* let us call action or next interceptor */
String result = invocation.invoke();
/* let us do some post-processing */
output = "Post-Processing";
System.out.println(output);
return result;
}
}
正如您所注意到的,实际操作将通过 invocation.invoke() 调用使用拦截器执行。因此,您可以根据需要进行一些预处理和后处理。
框架本身通过首次调用 ActionInvocation 对象的invoke() 来启动该过程。每次调用 invoke() 时,ActionInvocation 都会查阅其状态并执行接下来的拦截器。当所有配置的拦截器都被调用后,invoke() 方法将导致操作本身被执行。
下图通过请求流显示了相同的概念 −

创建操作类
让我们在 Java Resources > 下创建一个 java 文件 HelloWorldAction.java src 的包名为 com.tutorialspoint.struts2,其内容如下所示。
package com.tutorialspoint.struts2;
import com.opensymphony.xwork2.ActionSupport;
public class HelloWorldAction extends ActionSupport {
private String name;
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"的 execute 方法。
创建视图
让我们在 eclipse 项目的 WebContent 文件夹中创建以下 jsp 文件 HelloWorld.jsp。
<%@ page contentType = "text/html; charset = UTF-8" %>
<%@ taglib prefix = "s" uri = "/struts-tags" %>
<html>
<head>
<title>Hello World</title>
</head>
<body>
Hello World, <s:property value = "name"/>
</body>
</html>
创建主页
我们还需要在 WebContent 文件夹中创建 index.jsp。此文件将作为初始操作 URL,用户可以单击此 URL 来告诉 Struts 2 框架调用 HelloWorldAction 类中定义的方法并呈现 HelloWorld.jsp 视图。
<%@ page language = "java" contentType = "text/html; charset = ISO-8859-1"
pageEncoding = "ISO-8859-1"%>
<%@ taglib prefix = "s" uri = "/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Hello World</title>
</head>
<body>
<h1>Hello World From Struts2</h1>
<form action = "hello">
<label for = "name">Please enter your name</label><br/>
<input type = "text" name = "name"/>
<input type = "submit" value = "Say Hello"/>
</form>
</body>
</html>
上述视图文件中定义的 hello 操作将使用 struts.xml 文件映射到 HelloWorldAction 类及其执行方法。
配置文件
现在,我们需要注册我们的拦截器,然后像在上一个示例中调用默认拦截器一样调用它。要注册新定义的拦截器,<interceptors>...</interceptors> 标签直接放在 struts.xml 文件中的 <package> 标签下。您可以像在上一个示例中一样跳过此步骤来使用默认拦截器。但在这里让我们按如下方式注册和使用它 −
<?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.tutorialspoint.struts2.MyInterceptor" />
</interceptors>
<action name = "hello"
class = "com.tutorialspoint.struts2.HelloWorldAction"
method = "execute">
<interceptor-ref name = "params"/>
<interceptor-ref name = "myinterceptor" />
<result name = "success">/HelloWorld.jsp</result>
</action>
</package>
</struts>
需要注意的是,您可以在<package>标签内注册多个拦截器,同时您可以在<action>标签内调用多个拦截器。您可以使用不同的操作调用同一个拦截器。
需要在WebContent下的WEB-INF文件夹下创建web.xml文件,如下所示 −
<?xml version = "1.0" Encoding = "UTF-8"?>
<web-app xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xmlns = "http://java.sun.com/xml/ns/javaee"
xmlns:web = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id = "WebApp_ID" version = "3.0">
<display-name>Struts 2</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
右键单击项目名称,然后单击 Export > WAR File 以创建 War 文件。然后将此 WAR 部署到 Tomcat 的 webapps 目录中。最后,启动 Tomcat 服务器并尝试访问 URL http://localhost:8080/HelloWorldStruts2/index.jsp。这将产生以下屏幕 −

现在在给定的文本框中输入任何单词,然后单击 Say Hello 按钮以执行定义的操作。现在,如果您检查生成的日志,您会在底部找到以下文本 −
Pre-Processing
Inside action....
Post-Processing
堆叠多个拦截器
可以想象,必须为每个操作配置多个拦截器很快就会变得非常难以管理。因此,拦截器由拦截器堆栈管理。下面是一个例子,直接来自 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 name = "hello" class = "com.tutorialspoint.struts2.MyAction">
<interceptor-ref name = "basicStack"/>
<result>view.jsp</result>
</action
上述"basicStack"注册将注册所有六个带有 hello 操作的拦截器的完整堆栈。需要注意的是,拦截器是按照配置的顺序执行的。例如,在上述情况下,exception 将首先执行,其次是 servlet-config,依此类推。