Struts - 拦截器

  • 简述

    拦截器在概念上与 servlet 过滤器或 JDK 代理类相同。拦截器允许横切功能与动作和框架分开实现。您可以使用拦截器实现以下功能 -
    • 在调用操作之前提供预处理逻辑。
    • 在调用操作后提供后处理逻辑。
    • 捕获异常以便可以执行替代处理。
    中提供的许多功能 Struts2 框架是使用拦截器实现的;
    Examples 包括异常处理、文件上传、生命周期回调等。事实上,由于Struts2强调拦截器的大部分功能,它不太可能为每个动作分配7或8个拦截器。
  • Struts2 框架拦截器

    Struts 2 框架提供了一个很好的开箱即用拦截器列表,这些拦截器已经预先配置好并可以使用。下面列出了一些重要的拦截器 -
    序号 拦截器和描述
    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.jc2182.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 部署到 Tomcat 的 webapps 目录中。最后,启动Tomcat服务器并尝试访问URLhttp://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.
    
    这里的底线是因为 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() 方法的默认无操作实现。
  • 创建拦截器类

    让我们在中创建以下 MyInterceptor.java Java Resources > src 文件夹 -
    
    package com.jc2182.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文件HelloWorldAction.java Java Resources > src 带有包名 com.jc2182.struts2 与下面给出的内容。
    
    package com.jc2182.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 方法。
  • 创建视图

    让我们创建下面的jsp文件 HelloWorld.jsp 在 eclipse 项目的 WebContent 文件夹中。
    
    <%@ 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>
    
  • 创建主页

    我们还需要创建 index.jsp在 WebContent 文件夹中。该文件将用作初始操作 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> 标签直接放在 <package> 标签下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">
          <interceptors>
             <interceptor name = "myinterceptor"
                class = "com.jc2182.struts2.MyInterceptor" />
          </interceptors>
          <action name = "hello" 
             class = "com.jc2182.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 部署到 Tomcat 的 webapps 目录中。最后,启动Tomcat服务器并尝试访问URLhttp://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.jc2182.struts2.MyAction">
       <interceptor-ref name = "basicStack"/>
       <result>view.jsp</result>
    </action
    
    上面的“basicStack”注册将使用hello动作注册所有六个拦截器的完整权益。应该注意的是,拦截器是按照它们被配置的顺序执行的。例如,在上述情况下,异常将首先执行,其次是 servlet-config,依此类推。