java动态代理模式(jdk和cglib)

来源:互联网 发布:淘宝运营视频 编辑:程序博客网 时间:2024/06/08 16:45

为什么需要代理

假设有一个计算器类,类中定义了加,减,乘,除操作,然后我希望在每个方法执行前后打印一些提示信息,比如 “XX方法开始执行。。”,“XX方法结束执行。。”,要做到这一点,普通的解决方式是在每个方法前后加上 System.out.println("XX方法正在执行。。"),System.out.println("XX方法结束执行。。"),这样写起来十分麻烦,如果我们以后要扩充这个类,要加上乘方,开方等方法,还要再加上提示信息,代码及其冗余,也不便于维护。

代理模式可以解决这种囧境,利用这种模式,可以将一些通用的操作抽取出来,比如记录日志,权限管理等等,这些操作在很多地方都能用到,将这些操作单独拿出来,运行时动态加进去,这时我们希望的。

代理分为静态代理:主要通过继承和组合的方式实现,以及下面主要介绍的动态代理。

此外代理模式是Spring框架中AOP的基础,所以理解代理模式非常重要。

使用jdk自带的库来实现动态代理

被代理的类需要实现特定的接口,我们先定义这个接口:

package com.hunan.proxy;public interface Moveable {//move方法,参数为移动速度,返回移动的总路程public double  move(double speed);}

接下来我们写一个Handler为所有实现这个接口的类做代理,上面的接口中声明了move方法,我们希望再执行move方法前后记录日志,利用代理来将日志处理模块提取出来:

package com.hunan.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class LogHandler implements InvocationHandler {private Object target;public LogHandler(Object target) {super();this.target = target;}/* * 参数说明: * proxy 被代理的对象 * method 被代理对象的方法 * args 方法的参数 *  * 返回值: * Object  被代理的对象的方法的返回值 */@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 本例仅为演示动态代理,具体的日志处理过程就不具体实现了,仅打印一条提示信息。System.out.println("开始记录日志。。。");Object obj =  method.invoke(target, args);System.out.println("日志记录完毕。。。");return obj;}}

注意:代理是代理某个特定接口的实现类,而不是代理某个特定类,所以尽量用Object引用指向对象,使代码有更好的通用性。

第三步实现接口,用线程休眠的方式模拟汽车行驶:

package com.hunan.proxy;import java.util.Random;public class Car implements Moveable {@Overridepublic double move(double speed) {System.out.println("汽车行驶中。。。");int moveTime =  new Random().nextInt(3000);try {Thread.sleep(moveTime);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}return speed*moveTime;}}

最后写个main方法测试一下:

package com.hunan.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;public class Main {public static void main(String[] args) {Car car = new Car();InvocationHandler h = new LogHandler(car);Class<?> cls = car.getClass();/* * loader:类加载器 * interface:实现的接口 * h:InvocationHandler实例 */ Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);double distance = m.move(50);System.out.println("move:  " +  distance + " meters....");}}

运行结果如下:



基于cglib的动态代理

使用cglib来实现动态代理需要引入cglib的依赖包,这个网上很容易下载到。cglib的代理实现方式实际上是基于继承来实现的,它会创建一个被代理类的子类,在子类中添加日志处理等模块并调用父类的方法。这些子类由cglib帮我们管理。

仍然是代理前面的Moveable接口的实现类:

首先要实现MethodInterceptor接口:

package com.hunan.cglibproxy;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;public class CglibProxy implements MethodInterceptor {private Enhancer enhancer = new Enhancer();public Object getProxy(Class<?> clazz) {enhancer.setSuperclass(clazz);enhancer.setCallback(this);return enhancer.create();}/* * 拦截所有目标类方法的调用 * obj:目标类的实例 * m: 目标方法的反射对象 * args:方法的参数 * proxy:代理类的实例 * @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy) */@Overridepublic Object intercept(Object obj, Method m, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("日志开始。。。");//代理类调用父类方法Object distance= proxy.invokeSuper(obj, args);System.out.println("日志结束。。。");return distance;}}

这个enhancer就是用于实现代理的子类了,不过cglib帮我们包装了一下。

接下来写一个Train类实现Moveable接口:

package com.hunan.cglibproxy;import java.util.Random;import com.hunan.proxy.Moveable;public class Train implements Moveable {@Overridepublic double move(double speed) {System.out.println("火车行驶中。。。");int moveTime =  new Random().nextInt(3000);try {Thread.sleep(moveTime);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}return speed*moveTime;}}

写一个main方法测试一下:

package com.hunan.cglibproxy;public class Client {public static void main(String[] args) {CglibProxy proxy = new CglibProxy();Train t = (Train)proxy.getProxy(Train.class);double distance = t.move(100);System.out.println("火车行驶了: " + distance + "路程。。");}}

打印输出如下:


0 0