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

来源:互联网 发布:微信抽奖软件 编辑:程序博客网 时间:2024/05/22 09:42
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. }  

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 驾照正在考的时候住址变了怎么办 身份证到期了驾驶证上的号码怎么办 小车占道跟大车刮交警怎么办 邻居把消防栓的位置占用了怎么办 查环保要停业整改一个月怎么办 抽油烟机管道公共排烟道漏烟怎么办 深圳龙华电动车被交警扣了怎么办 英国平邮寄到中国丢件怎么办 安卓app与设备不兼容怎么办 手机卡坏了收不到快递短信怎么办 pph手术后钛钉没有脱落怎么办 右侧附件囊状透明声可怎么办 刚刚出生小孩睾丸没有掉下来怎么办 汽车被依非法营运之由扣押怎么办 老婆结婚小孩两个又上夜场该怎么办 对于不断无理要求赔偿的房东怎么办 欠的钱越来越多不知道怎么办了 手机壳的开机按键不好按怎么办 对方把保权的房子卖给我怎么办 内存卡在手机上显示损坏怎么办 内存卡显示但是下载不了东西怎么办 相机sd卡没办法读卡怎么办 尼康相机新sd卡显示满了怎么办 修冰箱没几个月又坏了怎么办 星露谷物语活动中心成了仓库怎么办 你家如果油锅起火时应该怎么办 当你遇到电器起火时应该怎么办 酷派大神f2充不进去电怎么办 借车子别人开违章一年了怎么办 快递刚发货我点错了收货怎么办 快递收货地址错了已经发货了怎么办 老公在外地工地上夜班吃不好怎么办 工人在工地干活被吊车撞了怎么办 小米手机记不得保密柜密码怎么办 华为手机文件保密柜密码忘了怎么办 超市寄存柜密码纸丢了怎么办 佳明软件安装不到手机上怎么办 最好的朋友的抢了自己的活怎么办 支付宝被实名验证上限了怎么办 装修预留插座被挡了一半怎么办 衣服放在柜子里面有味道怎么办