Struts 2 - 拦截器


拦截器在概念上与 servlet 过滤器或 JDK 代理类相同。拦截器允许横切功能与操作和框架分开实现。您可以使用拦截器实现以下目的 -

  • 在调用操作之前提供预处理逻辑。

  • 在调用操作后提供后处理逻辑。

  • 捕获异常以便可以执行替代处理。

Struts2框架中提供的很多功能都是使用拦截器来实现的;

示例包括异常处理、文件上传、生命周期回调等。事实上,由于 Struts2 强调其大部分功能都在拦截器上,因此不太可能为每个操作分配 7 或 8 个拦截器。

Struts2框架拦截器

Struts 2 框架提供了一系列开箱即用的拦截器,这些拦截器已预先配置并可供使用。下面列出了一些重要的拦截器 -

先生编号 拦截器和描述
1

别名

允许参数在请求之间具有不同的名称别名。

2

复选框

通过为未选中的复选框添加 false 参数值来协助管理复选框。

3

转换错误

将字符串转换为参数类型的错误信息放入操作的字段错误中。

4

创建会话

如果 HTTP 会话尚不存在,则自动创建。

5

调试

为开发人员提供多种不同的调试屏幕。

6

执行并等待

当操作在后台执行时,将用户发送到中间等待页面。

7

例外

将从操作引发的异常映射到结果,从而允许通过重定向进行自动异常处理。

8

上传文件

方便轻松上传文件。

9

国际化

在用户会话期间跟踪选定的区域设置。

10

记录器

通过输出正在执行的操作的名称来提供简单的日志记录。

11

参数

设置操作的请求参数。

12

准备

这通常用于执行预处理工作,例如设置数据库连接。

13

轮廓

允许记录操作的简单分析信息。

14

范围

在会话或应用程序范围中存储和检索操作的状态。

15

Servlet配置

为操作提供对各种基于 servlet 的信息的访问。

16

计时器

以操作执行所需时间的形式提供简单的分析信息。

17 号

代币

检查有效令牌的操作以防止重复的表单提交。

18

验证

为操作提供验证支持

请查看 Struts 2 文档以获取有关上述拦截器的完整详细信息。但我将向您展示如何在 Struts 应用程序中一般使用拦截器。

如何使用拦截器?

让我们看看如何在我们的“Hello World”程序中使用现有的拦截器。我们将使用计时器拦截器,其目的是测量执行操作方法所需的时间。同时,我使用params拦截器,其目的是将请求参数发送到操作。您可以在不使用此拦截器的情况下尝试示例,您会发现未设置name属性,因为参数无法到达操作。

我们将保留在示例章节中创建的 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.tutorialspoint.struts2.HelloWorldAction"
         method = "execute">
         <interceptor-ref name = "params"/>
         <interceptor-ref name = "timer" />
         <result name = "success">/HelloWorld.jsp</result>
      </action>
   </package>
</struts>

右键单击项目名称,然后单击“导出”>“WAR 文件”以创建 War 文件。然后将此 WAR 部署到 Tomcat 的 webapps 目录中。最后,启动 Tomcat 服务器并尝试访问 URL http://localhost:8080/HelloWorldStruts2/index.jsp。这将产生以下屏幕 -

你好世界Struts 4

现在,在给定文本框中输入任意单词,然后单击“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.

这里的底线是由于计时器拦截器而生成的,它告诉我们该操作总共花费了 109 毫秒来执行。

创建自定义拦截器

在应用程序中使用自定义拦截器是提供横切应用程序功能的一种优雅方式。创建自定义拦截器很容易;需要扩展的接口是以下拦截器接口 -

public interface Interceptor extends Serializable {
   void destroy();
   void init();
   String intercept(ActionInvocation invocation)
   throws Exception;
}

顾名思义,init() 方法提供了初始化拦截器的方法,destroy() 方法提供了拦截器清理的工具。与操作不同,拦截器在请求之间重用,并且需要线程安全,尤其是intercept()方法。

ActionIncation对象提供对运行时环境的访问它允许访问操作本身以及调用该操作的方法并确定该操作是否已被调用。

如果不需要初始化或清理代码,可以扩展AbstractInterceptor类。这提供了 init() 和 destroy() 方法的默认 nooperation 实现。

创建拦截器类

让我们在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;
   }
}

正如您所注意到的,实际操作将使用拦截器通过invoking.invoke()调用来执行。因此,您可以根据您的要求进行一些预处理和后处理。

框架本身通过第一次调用 ActionIn Vocation 对象的 invoke() 来启动该过程。每次调用invoke()时,ActionInitation 都会查阅其状态并执行接下来出现的拦截器。当所有配置的拦截器都被调用时,invoke() 方法将导致操作本身被执行。

下图通过请求流显示了相同的概念 -

动作调用

创建动作类

让我们在Java Resources > src下创建一个 java 文件 HelloWorldAction.java ,包名为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”的执行方法。

创建视图

让我们在 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>

右键单击项目名称,然后单击“导出”>“WAR 文件”以创建 War 文件。然后将此 WAR 部署到 Tomcat 的 webapps 目录中。最后,启动 Tomcat 服务器并尝试访问 URL http://localhost:8080/HelloWorldStruts2/index.jsp。这将产生以下屏幕 -

你好世界Struts 4

现在,在给定文本框中输入任意单词,然后单击“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 操作注册所有六个拦截器的完整权益。应该注意的是,拦截器是按照它们配置的顺序执行的。例如,在上面的情况下,异常将首先执行,第二个是 servlet-config 等等。