一、拦截器概述
拦截器,在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前进行拦截,然后在之前或之后加入某些操作。拦截器是AOP的一种实现策略。
在Webwork的中文文档的解释为:拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行。同时也是提供了一种可以提取action中可重用的部分的方式。
谈到拦截器,还有一个词大家应该知道——拦截器链(Interceptor Chain,在Struts 2中称为拦截器栈Interceptor Stack)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
二、拦截器的实现原理
大部分时候,拦截器方法都是通过代理的方式来调用的。Struts2的拦截器实现相对简单。当请求到达Struts 2的ServletDispatcher时,Struts 2会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串成一个列表(list),最后一个一个地调用列表中的拦截器。事实上,我们之所以能够如此灵活地使用拦截器,完全归功于“动态代理”的使用。动态代理是代理对象根据客户的需求做出不同的处理。对于客户来说,只要知道一个代理对象就行了。那Struts2中,拦截器是如何通过动态代理被调用的呢? 当Action请求到来的时候,会由系统的代理生成一个Action的代理对象,由这个代理对象调用Action的execute()或指定的方法,并在 struts.xml中查找与该Action对应的拦截器。如果有对应的拦截器,就在Action的方法执行前(后)调用这些拦截器;如果没有对应的拦截 器则执行Action的方法。其中系统对于拦截器的调用,是通过ActionInvocation来实现的。
Struts2拦截器是可插拔的,拦截器是AOP的一种实现。Struts2拦截器栈就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用。
三、自定义拦截器
3.1 实现Interceptor接口
public class MyInterceptor implements Interceptor { @Override // 初始化方法 public void init() { } @Override // 拦截方法 public String intercept(ActionInvocation arg0) throws Exception { return null; } @Override // 销毁方法 public void destroy() { } }
Interceptor接口提供了三个方法,其具体介绍如下
- void init():该方法在拦截器被创建后会立即调用,它在拦截器的生命周期内只被调用一次,可以在该方法中对相关资源进行必要的初始化。
- void destroy():该方法与init方法相对应,在拦截器实例被销毁之前,将调用该方法来释放和拦截器相关的资源。它在拦截器的生命周期内也只被调用一次。
- String interceptor(ActionInvocation invocation):该方法是拦截器的核心方法,用来添加真正执行拦截工作的代码,实现具体的拦截操作。它返回一个字符串作为逻辑视图,系统根据返回的字符串跳转到对应的视图资源。每拦截一个动作请求, 该方法就会被调用一次。该方法的ActionInvocation参数包含了被拦截的Action的引用,可以通过该参数的invoke()方法,将控制权转给下一个拦截器或者转给Action的execute()方法。
3.2 继承AbstractInterceptor
// 帮我们空实现了init和destory方法,如果我们不需要实现这两个方法,就可以只实现intercept方法 public class MyInterceptor extends AbstractInterceptor{ @Override public String intercept(ActionInvocation arg0) throws Exception { return null; } }
抽象类AbstractInterceptor已经实现了Interceptor接口的所有方法,一般情况下,只需继承AbstractInterceptor类,实现interceptor()方法就可以创建自定义拦截器。
只有当自定义的拦截器需要打开系统资源时,才需要覆盖AbstractInterceptor类的init()方法和destroy()方法。与实现Interceptor接口相比,继承AbstractInterceptor类的方法更为简单
3.3 继承MethodFilterInterceptor
// 功能:定制拦截器拦截的方法 // 定制哪些方法需要拦截,哪些方法不需要拦截 public class MyInterceptor extends MethodFilterInterceptor{ @Override protected String doIntercept(ActionInvocation invocation) throws Exception { // 前处理 System.out.println("MyInterceptor的前处理"); // 放行 String result = invocation.invoke(); // 后处理 System.out.println("MyInterceptor的后处理"); return result; // 不放行,直接跳转到一个结果页面(不执行后续的拦截器以及Action,直接交给Result处理结果.进行页面跳转) // return "success"; } }
MethodFilterInterceptor是AbstractInterceptor的子类,该类中提供了两个属性,可以告知拦截器对哪些方法进行拦截或者对哪些方法排除。这种方法在开发中最常用。
四、配置拦截器
<package name="interceptor" namespace="/" extends="struts-default"> <interceptors> <!-- 1.注册拦截器 --> <interceptor name="myInterceptor" class="cn.itcast.interceptor.MyInterceptor"></interceptor> <!-- 2.注册拦截器栈 --> <interceptor-stack name="myStack"> <!-- 引入自定义拦截器(建议放在20个拦截器之前) --> <interceptor-ref name="myInterceptor"> <!-- 指定哪些方法不拦截 <param name="excludeMethods">add,delete</param> --> <!-- 指定哪些方法要拦截 --> <param name="includeMethods">add,delete</param> </interceptor-ref> <!-- 引入默认的拦截器栈(20个) --> <interceptor-ref name="defaultStack"></interceptor-ref> </interceptor-stack> </interceptors> <!-- 3.指定包中的默认拦截器栈 --> <default-interceptor-ref name="myStack"></default-interceptor-ref> <action name="Demo1Action_*" class="cn.itcast.action.Demo1Action" method="{1}"> <result name="success" type="dispatcher">/index.jsp</result> </action> </package>