1、 拦截器的概念
Ø 拦截器(Interceptor)是动态拦截Action调用的对象,类似于Servlet中的过滤器。在执行Action的业务逻辑处理方法(execute())之前,Struts2会首先执行在struts.xml中引用的拦截器。
Ø 拦截器是struts2的一个重要特性。Struts2框架的大多数核心功能都是通过拦截器来实现的,像避免表单重复提交、类型转换、对象组装、验证、文件上传等,都是在拦截器的帮助下实现的。拦截器之所以称为“拦截器”,是因为它可以在Action执行之前和执行之后拦截调用。
Ø Struts2将它的核心功能放到拦截器中实现,而不是分散到Action中实现,有利于系统的解耦,使得功能的实现类似于个人电脑的组装,变成了可插拔的,需要某个功能就“插入”一个拦截器,不需要某个功能就“拔出”一个拦截器。你可以任意组合拦截器来为Action提供附加的功能,而不需要修改Action的代码。
2、 拦截器的工作方式
拦截器围绕着Action和Result的执行而执行,其工作方式如图:
从图中可以看到,在Action和Result执行之前,为Action配置的拦截器将首先被执行,在Action和Result执行之后,拦截器将重新获得控制权,然后按照与先前调用相反的顺序依次执行。在整个执行过程中,任何一个拦截器都可以选择直接返回,从而终止余下的拦截器、Action和Result的执行。例如:当一个未授权的用户访问受保护的资源时,执行身份验证的拦截器可以直接返回。
3、 编写拦截器类
在struts2中要编写拦截器类,必须实现com.opensymphony.xwork2.interceptor.Interceptor接口,该接口定义了如下的三个方法:
void init():
该方法在拦截器实例创建后、intercept()方法被调用之前调用,用于初始化拦截器所需要的资源,例如数据库连接的初始化。该方法只执行一次。
void destroy():
该方法在拦截器实例被销毁之前调用,用于释放在init()方法中分配的资源。该方法只执行一次。
String intercept(ActionInvocation invocation) throws Exception:
该方法在Action执行之前被调用,拦截器为Action提供的附加功能在该方法中实现。利用invocation参数,可以获取action执行的状态。在intercept()方法中,如果要继续执行后续的部分(包括余下的应用于Action的拦截器、Action和Result),可以调用invocation.invoke()(返回一个字符串,该字符串是Action中返回的字符串)。如果要终止后续的执行,可以直接返回一个结果码,框架将根据这个结果码来呈现对应的结果视图。
4、 例子
TimerInterceptor.java
public class TimerInterceptor implements Interceptor { public void destroy(){} public void init(){} public String intercept(ActionInvocation invocation) throws Exception { long startTime = System.currentTimeMillis(); String result = invocation.invoke(); long executionTime = System.currentTimeMillis() - startTime; System.out.println("Action的执行花费的毫秒数是:" + executionTime); return result; } } |
Struts.xml
<struts> <constant name="struts.i18n.encoding" value="GBK" /> <package name="default" extends="struts-default"> <interceptors> <interceptor name="time" class="com.ibm.action.TimerInterceptor"></interceptor> </interceptors> <action name="time" class="com.ibm.action.TimeAction"> <interceptor-ref name="time"></interceptor-ref> <result>index.jsp</result> </action> </package> </struts> |
说明:
这是一个非常简单的拦截器,用于输出Action执行花费的时间。在invocation.invoke()调用的前后,你可以添加自己的逻辑代码。Invocation.invoke()调用之前的代码将在Action执行之前执行,invocation.ivoke()调用之后的代码将在Action执行之后执行。
为了简化拦截器的开发,struts2还提供了一个抽象类:
com.opensymphony.xwork2.interceptor.AbstractInterceptor
它实现了Interceptor接口,并给出了init()和destroy()方法的空实现。我们编写的拦截器类也可以选择继承AbstractInterceptor类,如果不需要init()和destroy()方法,那么你只需要重写抽象的intercept()方法就可以了。
在struts2中还提供了一个特殊的拦截器抽象基类:
com.opensymphony.xwork2.interceptor.MethodFilterInterceptor
这个拦截器可以指定要拦截或排除的方法列表。通常情况下,拦截器将拦截Action的所有方法调用,但是在某些应用场景中,对某些方法的拦截将会出现一些问题。例如:我们只需要对Action中部分方法进行拦截,将要如何处理啦?
MethodFilterInterceptor通过指定include/excluded方法列表来选择拦截或排除的方法,可以设置的参数如下:
excludeMethods
要排除的方法
includeMethods
要拦截的方法
在设置拦截或排除的方法时,如果有多个方法,那么以逗号(,)分隔,如果一个方法的名字同时出现在excludeMethods和includeMethods参数中,那么会当作要拦截的方法。
例如:有如下的拦截器配置:
< package name =“default" extends ="struts-default" > <interceptors> <! - -在这里定义拦截器- -> <interceptor name=“around” class=“AroundInterceptor”/> </interceptors> < interceptor-ref name =“around”> <param name=“includeMethods”>select,update</param> <param name=“excludeMethods”>delete,insert</param> </interceptor-ref> </ action > </ package> |
4、拦截器栈
在很多时候,且些指定的拦截器需要被多个Action所使用,这个时候,如果我们为每一个Action都分别配置拦截器的话,不仅麻烦,而且不利后期的维护,此时就需要用到拦截器栈。
所谓拦截器栈就是将一些拦截器组合起来进行统一管理。
拦截器栈的使用与拦截器的使用非常相似,唯一有区别的地方,就是拦截器栈的布署方式。
<interceptors> <interceptor name=“permission" class="cn.itcast.aop.PermissionInterceptor" /> <interceptor-stack name="permissionStack"> <interceptor-ref name="defaultStack" /> <interceptor-ref name=" permission " /> </interceptor-stack> </interceptors> <action name="helloworld_*" class="cn.itcast.action.HelloWorldAction" method="{1}"> <result name="success">/WEB-INF/page/hello.jsp</result> <interceptor-ref name="permissionStack"/> </action> |
5、 内置的拦截器
6、配置拦截器
拦截器需要在struts.xml中进行配置才能为action提供服务。要为action配置引用拦截器,首先需要在interceptors元素中使用interceptor元素定义拦截器,然后 在action中使用interceptor-ref元素指定引用的拦截器。Interceptor元素有两个必需的属性:name和class,前者指定拦截器的名字,后者指定拦截器的完整类名。
比如对如前面的拦截器,需要如下配置:
< package name =“default" extends ="struts-default" > <interceptors> <! - -在这里定义拦截器- -> <interceptor name=“around” class=“AroundInterceptor”/> </interceptors> < interceptor-ref name =“around” /> <!– 此处引用拦截器 -- > </ package > |
如果在一个action需要多个拦截器,那又需要如何配置啦?另外,对于多个拦截器之间的执行顺序又是怎么样啦?
对于上述问题有二个解决方法:
(1)为一个Action配置多个拦截器
(2)将多个拦截器配为一个拦截器栈,然后在action中引用
< package name =“default" extends ="struts-default" > <interceptors> <! - -在这里定义拦截器- -> <interceptor name=“before” class=“BeforeInterceptor”/> <interceptor name=“after” class=“AfterInterceptor”/> <!—配置拦截器栈,便于使用à <interceptor-stack name=“mystack”> < interceptor-ref name =“before” /> < interceptor-ref name =“after” /> </interceptors> <default-interceptor-ref name=“mystack”/> < action name =“test” class =“cn.com.TestAction” > <!—如果配置了此拦截器,则默认的拦截器栈将失效à < interceptor-ref name =“after” /> </ action > </ package > |
6、 开发拦截器类
大家在开始着手创建自定义拦截器前,切记以下原则:
拦截器必须是无状态的,不要使用在API提供的ActionInvocation之外的任何东西。
原因:Struts 2不能保证为每一个请求或者action创建一个实例,所以如果拦截器带有状态,会引发并发问题。
在Struts2中编写拦截类,有如下二种方式:
1>实现Interceptor此接口
2>继承AbstractInterceptor类(一般会选择此种方式)
例如:
public class AroundInterceptor extends AbstractInterceptor { public void init(){System.out.println(“这是一个初始化方法”)} public String intercept(ActionInvocation invocation) throws Exception { String result=""; System.out.println("我是在action的execute方法执行之前动作"); result=invocation.invoke(); System.out.println("我是在action的execute方法执行之后动作"); return result; } public void destroy(){System.out.println(“这是一个清理方法”)} } 注意:在编写拦截器时要注意,拦截器必顺是无状态的,换句话 说,在拦截器类中不应该有实例变量。这是因为struts2对每一个Action的请求使用的是同一个拦截器实例来处理,如果拦截器有状态,在多线程并发情况下,拦截器的状态将不可测。
|