首页 > 代码库 > 谈谈 Struts2 的拦截器
谈谈 Struts2 的拦截器
套话
相信很多人都用过 Struts2 了,当然,对 Struts2 的原理也都比较了解。之前在一个项目中就已经用到了,当初的理解也仅仅是局限在应用的层面上,对于更深层次的原理、机制,了解的并不是很多。现在回过头来,再看 Struts2 的拦截器,相比之前的理解,又别有另一番滋味。
理解
Struts2 的拦截器,英文名为 Interceptor ,至于为什么中文要翻译为拦截器,我想这一定是有原因的,肯定不是一拍脑门,就叫拦截器了(它必定有什么含义在里边)。从字面的意思,就是在发送请求的时候,有一个什么东西把这个请求给拦截了,给挡下了,目的就是为了在请求之前、之后执行自己的方法(勿喷,大白话,仅供理解)。
拦截器可以动态地拦截发送到指定 Action 的请求,通过拦截机制,我们可以在 Action 执行的前后插入某些代码。通过这种方式,就可以把多个 Action 中需要重复指定的代码提取出来,放在拦截器里定义,从而提供更好的代码重用性。
意义
那么,拦截器有什么存在的必要呢?这个问题或许大家都没有想过,只知道 Struts2 的拦截器好用,只知道 Struts2 的拦截器怎么用而已。谈意义之前,首先讲一个规则,相信大家早就听说过了,其实一点也不新鲜 —— DRY 规则,就是所谓的“Don‘t Repeat Yourself”,意思大家都明白,就是“不要写重复的代码”。
拦截器的意义(之一)就在于此,可以这样理解:拦截器是对调用方法的改进。未使用拦截器时,代码中往往会需要显式的调用目标方法。如果把公共的部分抽象出来,定义成拦截器里的方法,就可以避免这样的耦合,直接由系统来调用。
实现原理
一般的说,拦截器都是通过代理的方式调用的。当请求到达 Struts2 的 ServletDispatcher 时,Struts2 会查找配置文件,并根据其配置实例化相对的拦截器对象,然后组成一个列表,最后一个一个地调用列表中的拦截器。
从本质上说,拦截器的实现主要是基于动态代理技术。用户发送 Action 请求时,系统会为 Action 创建一个代理对象,由这个代理对象调用 Action 的 execute() 或指定的方法,并在 struts.xml 中查找与该 Action 对应的拦截器。如果有对应的拦截器,就在 Action 的方法执行前(后)调用这些拦截器;如果没有对应的拦截器则执行 Action 的方法。其中系统对于拦截器的调用,是通过ActionInvocation来实现的。
代码演示
下面以 JDK动态代理为例,通过一个普通的 Java 程序,介绍如何调用拦截器的方法,给大家做一个演示,希望能给你带来帮助。
Dog 接口
<span style="font-family:Microsoft YaHei;">public interface Dog { // info 方法声明 public void info(); // run 方法声明 public void run(); }</span>
上面接口里简单定义了两个方法,由于 JDK 动态代理只能对实现了接口的实例来生成代理。因此必须提供一个接口。
Dog 实现
<span style="font-family:Microsoft YaHei;">public class DogImpl implements Dog { @Override public void info() { System.out.println("我是一只小小狗"); } @Override public void run() { System.out.println("我奔跑迅速"); } }</span>
该 Dog 的实现类仅仅为每个方法提供了一个简单实现,只是做了简单的打印。
拦截器类
<span style="font-family:Microsoft YaHei;">public class DogIntercepter { // 第一个拦截器方法 public void method1() { System.out.println("********** 通用模拟方法一 *********"); } // 第二个拦截器方法 public void method2() { System.out.println("********** 通用模拟方法二 *********"); } }</span>
其实,拦截器也是一个普通的 Java 类,之所以称为拦截器,主要是因为他的行为而命名的。
ProxyHandler 类
<span style="font-family:Microsoft YaHei;">public class ProxyHandler implements InvocationHandler { // 需被代理的目标对象 private Object target; // 创建拦截器实例 DogIntercepter di = new DogIntercepter(); // 执行代理的目标方法时,该invoke方法会被自动调用 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; // 如果被调用方法的方法名为info if ("info".equals(method.getName())) { // 调用拦截器方法 1 di.method1(); result = method.invoke(target, args); // 调用拦截器方法 2 di.method2(); } else { result = method.invoke(target, args); } return result; } // 用于设置传入目标对象的方法 public void setTarget(Object target) { this.target = target; } }</span>
该类是一个代理类,它需要实现 InvocationHandler接口,该接口是 JDK反射体系里的一个接口,它可以动态调用目标对象的方法。
代理工厂类
系统还需要提供一个代理工厂,代理工厂的主要作用就是根据目标对象生成一个代理对象。
Proxy.newProxyInstance() 方法根据接口数组动态创建代理类实例,接口数组通过 object.getClass().getInterfaces() 方法获得,创建的代理类是 JVM 在内存中动态创建,该类实现传入参数里接口数组中的全部接口。
从上面可以看出,代理工厂负责根据目标对象和对应的拦截器生成新的代理对象,代理对象里的方法是目标方法和拦截器方法的组合。正式通过这种方式,实现了在目标方法之前或之后,自动调用拦截器方法的目的。
主程序
<span style="font-family:Microsoft YaHei;">public class TestDog { /** * @param args */ public static void main(String[] args) { // 创建一个Dog实例,该实例将被作为代理的目标对象 Dog targetObject = new DogImpl(); Dog dog = null; // 以目标对象创建代理 Object proxy = MyProxyFactory.getProxy(targetObject); if (proxy instanceof Dog) { dog = (Dog) proxy; } // 测试代理的方法 dog.info(); dog.run(); } }</span>
效果图
结束语
这篇文章,是用最简单的文字和示例,来演示了 Struts2 拦截器的实现原理,当然,写的还有很多瑕疵,欢迎各位大神拍砖。By the way!通过这篇小文章,你是否想到了 Spring 的 AOP ? Struts2 的拦截器 和 Spring 的 AOP 有什么关系呢?你是否已经想到了。其实,Struts2 的拦截器 就是基于 AOP 的思想实现的,只不过,那会的 AOP 还没有提出比较明确的概念罢了。也可以说,拦截器就是 AOP 实现的前身。当然,后续的 AOP 功能更强大一些。
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。