1. struts2拦截器简介
1.1. 拦截器的概念
拦截器(Interceptor)是Struts2的核心组成部分。很多功能都是构建在拦截器基础之上的,例如文件的上传和下载、国际化、转换器和数据校验等,Struts2利用内建的拦截器,完成了框架内的大部分操作。
在Struts2文档中对拦截器的解释为--拦截器是动态拦截Action调用的对象。它提供了一种机制,使开发者可以定义一个特定的功能模块,这个模块可以在Action执行之前或者之后运行,也可以在一个Action执行之前阻止Action执行。同时也提供了一种可以提取Action中可重用的部分的方式。
拦截器是Struts2 更高层次的解耦,无须侵入框架本身便可以添加新的功能。
拦截器是AOP(Aspect-Oriented Programming)的一种实现,底层通过动态代理模式完成。
1.2. 拦截器的工作原理
Struts 2的拦截器实现相对简单。当请求到达Struts 2的ServletDispatcher时,Struts 2会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串成一个列表(list),最后一个一个地调用列表中的拦截器。事实上,我们之所以能够如此灵活地使用拦截器,完全归功于“动态代理”的使用。动态代理是代理对象根据客户的需求做出不同的处理。对于客户来说,只要知道一个代理对象就行了。那Struts2中,拦截器是如何通过动态代理被调用的呢?当Action请求到来的时候,会由系统的代理生成一个Action的代理对象,由这个代理对象调用Action的execute()或指定的方法,并在struts.xml中查找与该Action对应的拦截器。如果有对应的拦截器,就在Action的方法执行前(后)调用这些拦截器;如果没有对应的拦截器则执行Action的方法。其中系统对于拦截器的调用,是通过ActionInvocation来实现的。
1.3. 拦截器与过滤器的区别
Ø 拦截器是基于java的反射机制的,而过滤器是基于函数回调。
Ø 拦截器不依赖与servlet容器,而过滤器依赖与servlet容器。
Ø 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
Ø 拦截器可以访问action上下文、值栈里的对象,而过滤器不能。
Ø 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次
1.4. struts2内置拦截器
拦截器 | 名字 | 说明 |
Alias Interceptor | alias | 在不同请求之间将请求参数在不同名字件转换,请求内容不变 |
Chaining Interceptor | chain | 让前一个Action的属性可以被后一个Action访问,现在和chain类型的result()结合使用。 |
Checkbox Interceptor | checkbox | 添加了checkbox自动处理代码,将没有选中的checkbox的内容设定为false,而html默认情况下不提交没有选中的checkbox。 |
Cookies Interceptor | cookies | 使用配置的name,value来是指cookies |
Conversion Error Interceptor | conversionError | 将错误从ActionContext中添加到Action的属性字段中。 |
Create Session Interceptor | createSession | 自动的创建HttpSession,用来为需要使用到HttpSession的拦截器服务。 |
Debugging Interceptor | debugging | 提供不同的调试用的页面来展现内部的数据状况。 |
Execute and Wait Interceptor | execAndWait | 在后台执行Action,同时将用户带到一个中间的等待页面。 |
Exception Interceptor | exception | 将异常定位到一个画面 |
File Upload Interceptor | fileUpload | 提供文件上传功能 |
I18n Interceptor | i18n | 记录用户选择的locale |
Logger Interceptor | logger | 输出Action的名字 |
Message Store Interceptor | store | 存储或者访问实现ValidationAware接口的Action类出现的消息,错误,字段错误等。 |
Model Driven Interceptor | model-driven | 如果一个类实现了ModelDriven,将getModel得到的结果放在Value Stack中。 |
Scoped Model Driven | scoped-model-driven | 如果一个Action实现了ScopedModelDriven,则这个拦截器会从相应的Scope中取出model调用Action的setModel方法将其放入Action内部。 |
Parameters Interceptor | params | 将请求中的参数设置到Action中去。 |
Prepare Interceptor | prepare | 如果Acton实现了Preparable,则该拦截器调用Action类的prepare方法。 |
Scope Interceptor | scope | 将Action状态存入session和application的简单方法。 |
Servlet Config Interceptor | servletConfig | 提供访问HttpServletRequest和HttpServletResponse的方法,以Map的方式访问。 |
Static Parameters Interceptor | staticParams | 从struts.xml文件中将中的中的内容设置到对应的Action中。 |
Roles Interceptor | roles | 确定用户是否具有JAAS指定的Role,否则不予执行。 |
Timer Interceptor | timer | 输出Action执行的时间 |
Token Interceptor | token | 通过Token来避免双击 |
Token Session Interceptor | tokenSession | 和Token Interceptor一样,不过双击的时候把请求的数据存储在Session中 |
Validation Interceptor | validation | 使用action-validation.xml文件中定义的内容校验提交的数据。 |
Workflow Interceptor | workflow | 调用Action的validate方法,一旦有错误返回,重新定位到INPUT画面 |
Parameter Filter Interceptor | N/A | 从参数列表中删除不必要的参数 |
Profiling Interceptor | profiling | 通过参数激活profile |
2. 拦截器的配置
2.1. 配置拦截器
struts.xml
|
有的时候,如果需要在配置拦截器时就为其传入拦截器参数,其格式如下:
<interceptor name="拦截器名" class="拦截器实现类 "> <param name="参数名">参数值</param> ...//如果需要传入多个参数,可以一并设置 </interceptor> |
覆盖拦截器参数有三种方法:
1) 第一种方法,需要复制所有的默认的拦截器,然后在传递参数,比较少用。
|
2) 第二种方法,采用引用拦截器栈传参数
|
3) 第三种方法,采用引用拦截器传参数
|
2.2. 拦截器栈
在很多时候,有些指定的拦截器需要被多个Action所使用,这个时候,如果我们为每一个Action都分别配置拦截器的话,不仅麻烦,而且不利后期的维护,此时就需要用到拦截器栈。
所谓拦截器栈就是将一些拦截器组合起来进行统一管理。
<package name="default" extends="struts-default"> <interceptors> <interceptor name="timer" class=".."/> <interceptor name="logger" class=".."/> <interceptor-stack name="myStack"> <interceptor-ref name="timer"/> <interceptor-ref name="logger"/> </interceptor-stack> </interceptors>
<action name="login" class="tutuorial.Login"> <interceptor-ref name="myStack"/> <result name="input">login.jsp</result> <result name="success" type="redirectAction">/secure/home</result> </action> </package> |
2.3. 默认拦截器
拦截器栈配置完成后就可以在<action>中使用<interceptor-refname="myStack"/>对其引用了。
一旦继承了struts-default包(package),所有Action都会默认调用拦截器栈---defaultStack。但是当在Action配置中加入“<interceptor-ref name=”..“ />”则会覆盖defaultStack,所以在action中写拦截器引用时,需要显示引用defaultStack(而且最好在第一句)。
<default-interceptor-ref name=“…”>
如果为Action指定了拦截器,则默认拦截器不再起作用,必须显式指定默认截拦器。
3. 自定义拦截器
自定义拦截器有以下三种实现方法:
1) 实现Interceptor接口
2) 继承AbstractInterceptor类
3) 继承MethodFilterInterceptor类
3.1. 实现Interceptor接口
com.opensymphony.xwork2.interceptor.Interceptor接口的代码如下:
public interface Interceptor extends Serializable { void destroy(); void init(); String intercept(ActionInvocation invocation) throws Exception; } |
该接口中有如下三种方法:
1) init():该方法在拦截器被实例化之后、拦截器执行之前调用。该方法只被执行一次,主要用于初始化资源。
2) intercept(ActionInvocationinvocation):该方法用于实现拦截的动作。该方法有个参数,用该参数调用invoke()方法,将控制权交给下一个拦截器,或者交给Action类的方法。
3) destroy():该方法与init()方法对应,拦截器实例被销毁之前调用。用于销毁在init()方法中打开的资源。
3.2. 继承AbstractInterceptor类
com.opensymphony.xwork2.interceptor.AbstractInterceptor抽象类代码如下:
public abstract class AbstractInterceptor implements Interceptor { public void init() {} public void destroy() { } public abstract String intercept(ActionInvocation invocation) throws Exception; } |
该抽象类实现了Interceptor接口,并提供了init()方法和destroy()方法的空实现。在一般的拦截器实现中,都会继承该类,因为一般实现的拦截器是不需要打开资源的,故无须实现这两种方法,继承该类会更简洁。
3.3. 使用自定义拦截器解决中文乱码
1) 自定义拦截器类的关键实现
public class EncodingInterceptor extends AbstractInterceptor {
public String intercept(ActionInvocation invocation) throws Exception {
HttpServletRequest request = (HttpServletRequest) invocation .getInvocationContext().get(ServletActionContext.HTTP_REQUEST); Map<String, String[]> paramMap = request.getParameterMap(); Iterator<String[]> iterable = paramMap.values().iterator(); while (iterable.hasNext()) { String[] values = iterable.next();
for (int i = 0; i < values.length; i++) { values[i] = new String(values[i].getBytes("iso-8859-1"), "utf-8"); } } return invocation.invoke(); }
} |
2) 自定义拦截器的配置,struts.xml
<package name="user" extends="struts-default" namespace="/"> <interceptors> <interceptor name="encodingInteceptor" class="com.morris.interceptor.EncodingInterceptor"> </interceptor> <interceptor-stack name="userStack"> <interceptor-ref name="encodingInteceptor"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors>
<default-interceptor-ref name="userStack"></default-interceptor-ref> <action name="…." class="….""> ……………… </action> </package> |
3.4. 继承MethodFilterInterceptor类
Struts 2框架还提供了com.opensymphony.xwork2.interceptor.MethodFilterInterceptor抽象类,该类继承了AbstractInterceptor类,这个拦截器可以指定要拦截或排除的方法列表。通常情况下,拦截器将拦截Action的所有方法调用,但在某些应用场景中,对某些方法的拦截将会出现一些问题。例如:对表单字段进行验证的拦截器,当我们通过doDefault()方法输出表单时,该方法不应该被拦截,因此此时表单字段都没有数据。该类部分代码如下:
public abstract class MethodFilterInterceptor extends AbstractInterceptor{ protected Set<String> excludeMethods = Collections.emptySet(); protected Set<String> includeMethods = Collections.emptySet(); 。。。 protected abstract String doIntercept(ActionInvocation invocation) throws Exception; 。。。。。 }
|
在使用的时候我们只需要继承MethodFilterInterceptor类:重写doIntercept方法即可。
拦截器的配置如下:
<interceptor name=”” class=””> <!– m1, m3, m4需要拦截 --> <param name=”includeMethods”>m1, m3, m4</param> <!-- m2不需要拦截 --> <param name=”excludeMethods”>m2</param> </interceptor>
|
在struts2中,从MethodFilterInterceptor继承的拦截器类有:
Ø TokenInterceptor
Ø TokenSessionStoreInterceptor
Ø DefaultWorkflowInterceptor
Ø ExecuteAndWaitInterceptor
Ø ValidationInterceptor
Ø ParametersInterceptor
Ø PrepareInterceptor
MethodFilterInterceptor通过指定included/excluded方法列表来选择拦截器或排除的方法,可以设置的参数如下:
excludeMethods-------要排除的方法。
includeMethods--------要拦截的方法。
例如:有如下的拦截器配置:
<interceptor-ref name="validation"> <param name="excludeMethods">input,back,cancel</param> <param name="includeMethods">execute</param> </interceptor-ref> |
当执行Action的input、back和cancel方法时,验证拦截器将不执行对输入数据的验证。当执行Action的execute方法时,验证拦截器将执行对输入数据的验证。
在设置拦截器或排除的方法时,如果有多个方法,那么以逗号(,)分隔,如上所示。如果一个方法的名字同时出现在execludeMethods和includeMethods参数中,那么它会被当作要拦截的方法。也就是说, includeMethods优先于execludeMethods。
在编写拦截器类的时候要注意,拦截器必须是无状态的,换句话说,在拦截器类中不应该有实例变量。这是因为struts2对每一个Action的请求使用的是同一个拦截器实例来拦截调用,如果拦截器有状态,在多个线程(客户端的每个请求将由服务器端的一个线程来服务)同时访问一个拦截器实例的情况下,拦截器的状态将不可预测。
word版本下载 源代码下载