拦截器
拦截器在概念上和servlet过滤器或JDKs代理类一样。拦截器允许横切功能在动作和框架中单独实现。你可以使用拦截器实现下面的内容:
-
在动作被调用之前提供预处理逻辑。
-
在动作被调用之后提供预处理逻辑。
- 捕获异常,以便可以执行交替处理。
Struts 2 框架提供的许多功能都是使用拦截实现的;例如包括异常处理,文件上传,生命周期回调和验证等。事实上,由于 Struts 2 是许多拦截器功能的基础,所以每次动作不是不可能有 7 个或 8 个拦截器被分配。
Struts 2 框架的拦截器
Struts 2 框架提供了一列开箱即用的拦截器来预先设定和准备使用。下面列出了几个重要的拦截器:
序号 | 拦截器及描述 |
---|---|
1 |
alias
允许参数有不同的跨请求的别名。 |
2 |
checkbox
通过为没有被检查的复选框添加一个参数值 false 来协助管理复选框。 |
3 |
conversionError
把从字符串转化为参数类型的错误信息放置到动作的字段错误中。 |
4 |
createSession
如果不存在 HTTP 会话,则自动创建一个 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.Struts 2.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 目录下部署这个 WAR。最后,启动 Tomcat 服务器和尝试访问 URL http://localhost:8080/HelloWorldStruts 2/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 拦截器生成长的,它告诉动作被执行的时间的 109ms。
创建自定义的拦截器
在你的应用程序中使用自定义的拦截器是一种提供横切的应用功能的简洁的方式。创建一个自定义的拦截器是很容易的,需要扩展的接口是下面的 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.Struts 2;
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 > src 中名为 com.tutorialspoint.Struts 2 的包下创建一个 java 文件HelloWorldAction.java,它的内容在下面给出。
package com.tutorialspoint.Struts 2;
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” 属性有标准的 getters 和 setters 方法,还有返回字符串 “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,用户可以点击它告诉 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 Struts 2</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 类和它的 execute 方法中。
配置文件
现在,我们需要注册我们的拦截器,然后调用它,就如我们已经在前面的例子中调用了默认的拦截器一样。为了注册一个新定义的拦截器,可以把
<?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.Struts 2.MyInterceptor" /> </interceptors> <action name="hello" class="com.tutorialspoint.Struts 2.HelloWorldAction" method="execute"> <interceptor-ref name="params"/> <interceptor-ref name="myinterceptor" /> <result name="success">/HelloWorld.jsp</result> </action> </package> </struts>
应该注意的是,你可以在
web.xml 文件需要在 WebContent 的 WEB-INF 文件夹下创建,如下所示:
<?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>Struts 2</filter-name>
<filter-class>
org.apache.Struts 2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>Struts 2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
右键单击项目名称,并且单击 Export > WAR File 来创建一个 War文件。然后在 Tomcat 的 webapps 目录下部署这个 WAR。最后,启动Tomcat 服务器和尝试访问 URL http://localhost:8080/HelloWorldStruts 2/index.jsp/
将会给出下面的画面:
现在,在给定的文本框中输入任何单词,并且单击 Say Hello 按钮执行已经定义的动作。现在,如果你查看生成的日志,就会在底部发现下面的文字:
Pre-Processing
Inside action....
Post-Processing
堆叠多个拦截器
可想而知,为每个动作配置多个拦截器很快就会变得非常难以管理。为此,拦截器用拦截器栈进行管理。这儿有一个例子,直接来自 struts-default.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,它可以用在你的配置中,如下所示。这个配置节点被放置在
我们已经看到了如何应用拦截器到动作中,应用拦截器栈是没有什么不同的。实际上,我们完全使用相同的标签:
<action name="hello" class="com.tutorialspoint.Struts 2.MyAction">
<interceptor-ref name="basicStack"/>
<result>view.jsp</result>
</action
上述注册的 “basicStack” 将完整地注册所有带有 hello 动作的 6 个拦截器。应该指出的是,拦截器按照已配置的顺序执行。例如,在上述情况下,将首先执行异常,第二个执行的是 servlet 配置等等。