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的请求使用的是同一个拦截器实例来拦截调用。假设拦截器有状态,在多个线程(client的每个请求将由server端的一个线程来服务)同一时候訪问一个拦截器实例的情况下。拦截器的状态将不可预測。