实现自己的可重用拦截器机制

本文介绍如何使用 Java 的 Proxy 和 InvocationHandler 接口实现 AOP 功能,通过示例展示了如何创建动态代理并实现自定义拦截器。
AOP技术是spring框架的一个重要特征。通过该特性能够在 函数运行之前,之后,或者异常处理的时候 执行我们需要的一些操作。 

下面我们就是需要抛开AOP,Spring这样成型的框架不用,而仅仅使用java反射机制中的Proxy,InvocationHandler来实现类似Spring框架的拦截器的效果。 

动态代理DynamicProxy  

首先,在设计这个拦截器框架之前,我们需要明白java中动态代理是什么?我想如果早就清楚请直接跳过,如果需要了解,那我想你手边最好有一个javadoc的电子书。 

Java.lang.reflect.Proxy是反射包的成员之一。具体说明请查javadoc。 
用法就是比如有一个对象,我们需要在调用 它提供的方法 之前,干点别的什么,就不能直接调用它,而是生成一个它的代理,这个代理有 这个对象所提供的所有接口方法 ,我们通过直接调用代理的这些方法,来实现: 函数既能像原来对象的那样工作,又能在函数运行过程前后加入我们自己的处理。  

这个类有个非常重要的函数用来实现某个类的代理: 
Java代码   收藏代码
  1. Object java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader,   
  2.              Class<?>[] interfaces,   
  3.              InvocationHandler h) throws IllegalArgumentException  


参数有点迷惑人,解释下: 
ClassLoader  是类加载器,这个参数用来定义代理类,一般使用原对象的即可,也可以为null用上下文解决。 
Class<?>[]  接口数组,就是我们需要这个代理能够提供原来的类的什么函数。如果全部则直接 class.getInterfaces() 来解决. 
InvocationHandler  调用处理器,这个就是如果你调用代理的方法,那么这个处理器就会被关联过来,处理调用这个函数的整个过程。这个接口只定义了一个方法: 
Java代码   收藏代码
  1. public Object invoke(Object proxy, Method method,   
  2.                   Object[] args) throws Throwable;  

参数中 proxy 就是你调用的代理, method 指的是你调用的代理的那个方法, args 是传给该方法的参数。 

我们生成某个类的代理步骤,一般需要先考虑我们在调用这个类的函数的时候(之前,或者之后)如何处理某些事情,因此我们首先考虑的就是如何实现 InvocationHandler 这个接口。 

让我们做一个实践,做这么一个调用处理器:任何使用此处理器的代理在调用它的任何方法的时候,都打印 被代理的类的类名+“.”+方法名+”(“+参数+”,”+参数+...+”)” 。 

步骤1: 定义接口 IUser  
Java代码   收藏代码
  1. package com.cyh.proxy.sample;  
  2.   
  3. public interface IUser {  
  4.     public String getName();  
  5.   
  6.     public void setName(String name);  
  7. }  


步骤2: 写IUser接口的实现类 User  
Java代码   收藏代码
  1. package com.cyh.proxy.sample.impl;  
  2.   
  3. import com.cyh.proxy.sample.IUser;  
  4.   
  5. public class User implements IUser {  
  6.     String name;  
  7.   
  8.     public User(String name) {  
  9.     this.name = name;  
  10.     }  
  11.   
  12.     public String getName() {  
  13.     return name;  
  14.     }  
  15.   
  16.     public void setName(String name) {  
  17.     this.name = name;  
  18.     }  
  19. }  


步骤3: 写 TraceHandler 实现调用处理器 InvocationHandler ,即在 invoke() 方法里我们要打印被 代理的类的类名+“.”+方法名+”(“+参数+”,”+参数+...+”)” 。 

Java代码   收藏代码
  1. package com.cyh.proxy.sample.impl;  
  2.   
  3. import java.lang.reflect.InvocationHandler;  
  4. import java.lang.reflect.Method;  
  5.   
  6. public class TraceHandler implements InvocationHandler {  
  7.     private Object target;  
  8.   
  9.     public TraceHandler(Object target) {  
  10.     this.target = target;  
  11.     }  
  12.   
  13.     public Object invoke(Object proxy, Method method, Object[] args)  
  14.         throws Throwable {  
  15.   
  16.     // print implicit argument  
  17.     System.out.print(target.getClass().getName());  
  18.     // print method name  
  19.     System.out.print("." + method.getName() + "(");  
  20.     // print explicit arguments  
  21.     if (args != null) {  
  22.         for (int i = 0; i < args.length; i++) {  
  23.         System.out.print(args[i]);  
  24.         if (i < args.length - 1) {  
  25.             System.out.print(",");  
  26.         }  
  27.         }  
  28.     }  
  29.     System.out.println(")");  
  30.   
  31.     return method.invoke(this.target, args);  
  32.     }  
  33. }  


步骤4: 最后,让我们写测试类 ProxyTest  
Java代码   收藏代码
  1. package com.cyh.proxy.sample.test;  
  2.   
  3. import java.lang.reflect.InvocationHandler;  
  4. import java.lang.reflect.Proxy;  
  5.   
  6. import com.cyh.proxy.sample.IUser;  
  7. import com.cyh.proxy.sample.impl.TraceHandler;  
  8. import com.cyh.proxy.sample.impl.User;  
  9.   
  10. public class ProxyTest {  
  11.     User user;  
  12.   
  13.     public ProxyTest() {  
  14.     user = new User("LaraCroft");  
  15.   
  16.     ClassLoader classLoader = user.getClass().getClassLoader();  
  17.     Class[] interfaces = user.getClass().getInterfaces();  
  18.     InvocationHandler handler = new TraceHandler(user);  
  19.     IUser proxy = (IUser) Proxy.newProxyInstance(classLoader, interfaces,  
  20.         handler);  
  21.   
  22.     proxy.setName("David Beckham");  
  23.     }  
  24.   
  25.     public static void main(String[] args) {  
  26.     new ProxyTest();  
  27.     }  
  28.   
  29. }  



好了,所有代码写好了,运行一下,测试结果是: 
com.cyh.proxy.impl.User.setName(David Beckham)  

讲一下运行原理: 
首先我们初始化了user对象,user.name = = “LaraCroft”; 
然后创建了user对象的代理proxy。 
注意这里: Proxy.newProxyInstance()函数的返回值使用接口IUser转型的,你或许会想到 
用User来做强制类型转换,但是会抛出下面的异常
 
Exception in thread "main" java.lang.ClassCastException: $Proxy0 cannot be cast to com.cyh.proxy.impl.User  
因为:代理类是实现了User类的所有接口,但是它的类型是$Proxy0,不是User。  

最后,我们调用代理的setName()方法: 
proxy.setName("David Beckham");  

代理在执行此方法的时候,就好触发调用处理器 TraceHandler,并执行 TraceHandler的invoke()方法,然后就会打印: 
com.cyh.proxy.impl.User.setName(David Beckham)  



可重用拦截器机制的实现  


好了,关于代理的知识我们讲完了,我们可以考虑如何实现这个拦截器的框架,所谓 拦截器就是在函数的运行前后定制自己的处理行为 ,也就是通过实现 InvocationHandler 达到的。 


设计思路  


我们来理清一下思路,在使用一个拦截器的时候? 什么是不变的,什么是变化的?  

不变的:  
每次都要创建代理 
拦截的时间:函数执行之前,之后,异常处理的时候 

变化的:  
每次代理的对象不同 
拦截器每次拦截到执行时的操作不同 

好了,废话少说,看类图: 

 

图中: 
DynamicProxyFactory  和它的实现类,是一个工厂,用来创建代理 

Interceptor  这个接口用来定义拦截器的拦截处理行为配合DynamicProxyInvocationHandler达到拦截效果 
DynamicProxyInvocationHandler  调用处理器的实现,它有两个成员,一个是Object target指的是被代理的类,另一个是Interceptor interceptor就是在invoke()方法执行target的函数之前后,异常处理时,调用interceptor的实现来达到拦截,并处理的效果。 


代码实现  

步骤1: 定义接口 DynamicProxyFactory  
Java代码   收藏代码
  1. package com.cyh.proxy.interceptor;  
  2.   
  3. public interface DynamicProxyFactory {  
  4.     /** 
  5.      * 生成动态代理,并且在调用代理执行函数的时候使用拦截器 
  6.      *  
  7.      * @param clazz 
  8.      *            需要实现的接口 
  9.      * @param target 
  10.      *            实现此接口的类 
  11.      * @param interceptor 
  12.      *            拦截器 
  13.      * @return 
  14.      */  
  15.     public <T> T createProxy(T target, Interceptor interceptor);  
  16. }  


步骤2: 定义接口 Interceptor  
Java代码   收藏代码
  1. package com.cyh.proxy.interceptor;  
  2.   
  3. import java.lang.reflect.Method;  
  4.   
  5. public interface Interceptor {  
  6.     public void before(Method method, Object[] args);  
  7.   
  8.     public void after(Method method, Object[] args);  
  9.   
  10.     public void afterThrowing(Method method, Object[] args, Throwable throwable);  
  11.   
  12.     public void afterFinally(Method method, Object[] args);  
  13. }  


步骤3: 实现接口 DynamicProxyFactory  
Java代码   收藏代码
  1. package com.cyh.proxy.interceptor.impl;  
  2.   
  3. import java.lang.reflect.InvocationHandler;  
  4. import java.lang.reflect.Proxy;  
  5.   
  6. import com.cyh.proxy.interceptor.DynamicProxyFactory;  
  7. import com.cyh.proxy.interceptor.Interceptor;  
  8.   
  9. public class DynamicProxyFactoryImpl implements DynamicProxyFactory {  
  10.     /** 
  11.      * 生成动态代理,并且在调用代理执行函数的时候使用拦截器 
  12.      *  
  13.      * @param target 
  14.      *  需要代理的实例 
  15.      * @param interceptor 
  16.      *  拦截器实现,就是我们希望代理类执行函数的前后, 
  17.      *  抛出异常,finally的时候去做写什么 
  18.      */  
  19.     @Override  
  20.     @SuppressWarnings("unchecked")  
  21.     public <T> T createProxy(T target, Interceptor interceptor) {  
  22.     // 当前对象的类加载器  
  23.     ClassLoader classLoader = target.getClass().getClassLoader();  
  24.     // 获取此对象实现的所有接口  
  25.     Class<?>[] interfaces = target.getClass().getInterfaces();  
  26.     // 利用DynamicProxyInvocationHandler类来实现InvocationHandler  
  27.     InvocationHandler handler = new DynamicProxyInvocationHandler(target,  
  28.         interceptor);  
  29.   
  30.     return (T) Proxy.newProxyInstance(classLoader, interfaces, handler);  
  31.     }  
  32. }  


步骤4: 实现调用处理器 
Java代码   收藏代码
  1. package com.cyh.proxy.interceptor.impl;  
  2.   
  3. import java.lang.reflect.InvocationHandler;  
  4. import java.lang.reflect.Method;  
  5.   
  6. import com.cyh.proxy.interceptor.Interceptor;  
  7.   
  8. /** 
  9.  * 动态代理的调用处理器 
  10.  *  
  11.  * @author chen.yinghua 
  12.  */  
  13. public class DynamicProxyInvocationHandler implements InvocationHandler {  
  14.     private Object target;  
  15.     private Interceptor interceptor;  
  16.   
  17.     /** 
  18.      * @param target 
  19.      *            需要代理的实例 
  20.      * @param interceptor 
  21.      *            拦截器 
  22.      */  
  23.     public DynamicProxyInvocationHandler(Object target,  
  24.                                   Interceptor interceptor) {  
  25.     this.target = target;  
  26.     this.interceptor = interceptor;  
  27.     }  
  28.   
  29.     /** 
  30.      * @param proxy 
  31.      *            所生成的代理对象 
  32.      * @param method 
  33.      *            调用的方法示例 
  34.      * @args args 参数数组 
  35.      * @Override 
  36.      */  
  37.     public Object invoke(Object proxy, Method method, Object[] args)  
  38.         throws Throwable {  
  39.     Object result = null;  
  40.   
  41.     try {  
  42.         // 在执行method之前调用interceptor去做什么事  
  43.         this.interceptor.before(method, args);  
  44.         // 在这里我们调用原始实例的method  
  45.         result = method.invoke(this.target, args);  
  46.         // 在执行method之后调用interceptor去做什么事  
  47.         this.interceptor.after(method, args);  
  48.     } catch (Throwable throwable) {  
  49.         // 在发生异常之后调用interceptor去做什么事  
  50.         this.interceptor.afterThrowing(method, args, throwable);  
  51.         throw throwable;  
  52.     } finally {  
  53.         // 在finally之后调用interceptor去做什么事  
  54.         interceptor.afterFinally(method, args);  
  55.     }  
  56.   
  57.     return result;  
  58.     }  
  59.   
  60. }  


好了,目前为止,这个框架算完成了,怎么用呢? 
接下来我们完成测试包。 

完成测试  

步骤1: 首先,给需要代理的类定义一个接口 Service  
Java代码   收藏代码
  1. package com.cyh.proxy.interceptor.test;  
  2.   
  3. public interface Service {  
  4.     public String greet(String name);  
  5. }  

步骤2: 实现这个接口,编写类 ServiceImpl  
Java代码   收藏代码
  1. package com.cyh.proxy.interceptor.test;  
  2.   
  3. public class ServiceImpl implements Service {  
  4.     @Override  
  5.     public String greet(String name) {  
  6.     String result = "Hello, " + name;  
  7.     System.out.println(result);  
  8.     return result;  
  9.     }  
  10. }  

步骤3: 实现拦截器接口Interceptor,编写类 InterceptorImpl  
Java代码   收藏代码
  1. package com.cyh.proxy.interceptor.test;  
  2.   
  3. import java.lang.reflect.Method;  
  4.   
  5. import com.cyh.proxy.interceptor.Interceptor;  
  6.   
  7. public class InterceptorImpl implements Interceptor {  
  8.     @Override  
  9.     public void after(Method method, Object[] args) {  
  10.     System.out.println("after invoking method: " + method.getName());  
  11.     }  
  12.   
  13.     @Override  
  14.     public void afterFinally(Method method, Object[] args) {  
  15.     System.out.println("afterFinally invoking method: " + method.getName());  
  16.     }  
  17.   
  18.     @Override  
  19.     public void afterThrowing(Method method, Object[] args,   
  20.                                 Throwable throwable) {  
  21.     System.out.println("afterThrowing invoking method: "  
  22.                                                 + method.getName());  
  23.     }  
  24.   
  25.     @Override  
  26.     public void before(Method method, Object[] args) {  
  27.     System.out.println("before invoking method: " + method.getName());  
  28.     }  
  29. }  

步骤4:编写测试类 TestDynamicProxy  
Java代码   收藏代码
  1. package com.cyh.proxy.interceptor.test;  
  2.   
  3. import com.cyh.proxy.interceptor.DynamicProxyFactory;  
  4. import com.cyh.proxy.interceptor.Interceptor;  
  5. import com.cyh.proxy.interceptor.impl.DynamicProxyFactoryImpl;  
  6.   
  7. public class TestDynamicProxy {  
  8.     public TestDynamicProxy() {  
  9.     DynamicProxyFactory dynamicProxyFactory = new DynamicProxyFactoryImpl();  
  10.     Interceptor interceptor = new InterceptorImpl();  
  11.     Service service = new ServiceImpl();  
  12.   
  13.     Service proxy = dynamicProxyFactory.createProxy(service, interceptor);  
  14.     // Service proxy = DefaultProxyFactory.createProxy(service,  
  15.     // interceptor);  
  16.     proxy.greet("iwindyforest");  
  17.     }  
  18.   
  19.     public static void main(String[] args) {  
  20.     new TestDynamicProxy();  
  21.     }  
  22. }  


好了,整个测试包完成了,让我们运行下看看运行结果: 

before invoking method: greet 
Hello, iwindyforest 
after invoking method: greet 
afterFinally invoking method: greet 



完善设计  

现在,让我们回顾一下:接口 DynamicProxyFactory ,真的需要么? 
它只是一个工厂,负责生产代理的,但是我们并没有过多的要求,因此可以说它的实现基本上是不变的。鉴于此,我们在使用 createProxy() 函数的时候,只需要一个 静态方法 就可以了,没有必要再初始化整个类,这样才比较方便么。 
因此,我在 com.cyh.proxy.interceptor.impl 包里加了一个默认的工厂 DefaultProxyFactory : 
Java代码   收藏代码
  1. package com.cyh.proxy.interceptor.impl;  
  2.   
  3. import java.lang.reflect.InvocationHandler;  
  4. import java.lang.reflect.Proxy;  
  5.   
  6. import com.cyh.proxy.interceptor.Interceptor;  
  7.   
  8. public class DefaultProxyFactory {  
  9.     @SuppressWarnings("unchecked")  
  10.     public static <T> T createProxy(T target, Interceptor interceptor) {  
  11.     // 当前对象的类加载器  
  12.     ClassLoader classLoader = target.getClass().getClassLoader();  
  13.     // 获取此对象实现的所有接口  
  14.     Class<?>[] interfaces = target.getClass().getInterfaces();  
  15.     // 利用DynamicProxyInvocationHandler类来实现InvocationHandler  
  16.     InvocationHandler handler = new DynamicProxyInvocationHandler(target,  
  17.         interceptor);  
  18.   
  19.     return (T) Proxy.newProxyInstance(classLoader, interfaces, handler);  
  20.     }  
  21. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值