动态代理

来源:互联网 发布:ip网络广播软件 编辑:程序博客网 时间:2024/05/23 13:33

在代理模式那篇文章里,我们了解的代理模式都是基于静态代理实现的,即需要我们手动实现代理类。今天我们来讲讲动态代理,不用我们代码实现代理类,而是交给程序运行时动态的创建。

JDK动态代理

JDK动态代理是java本身提供的一种动态代理机制,主要涉及到核心接口InvocationHandler和类Proxy。
我们依旧拿游戏的例子来讲讲JDK动态代理如何实现的,首先定义一个处理类,实现InvocationHandler接口:

package com.zz.aop.JDKDynamicsProxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import design_pattern.proxyPattern.easy.IGamePlayerAction;public class GamePlayerProxyHandler implements InvocationHandler {    //持有被代理对象    private IGamePlayerAction iGamePlayer;    public GamePlayerProxyHandler(IGamePlayerAction iGamePlayer) {        super();        this.iGamePlayer = iGamePlayer;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args)            throws Throwable {        return method.invoke(iGamePlayer, args);    }}

场景类中模拟游戏过程:

package com.zz.aop.JDKDynamicsProxy;import java.lang.reflect.Proxy;import design_pattern.proxyPattern.easy.GamePlayer;import design_pattern.proxyPattern.easy.IGamePlayerAction;public class Client {    public static void main(String[] args) {        //获取被代理对象        IGamePlayerAction iGamePlayer = new GamePlayer("neil");        //获取被代理对象的类加载器        ClassLoader loader = iGamePlayer.getClass().getClassLoader();        //获取一个实际处理类        GamePlayerProxyHandler handler = new GamePlayerProxyHandler(iGamePlayer);        IGamePlayerAction proxy = (IGamePlayerAction) Proxy.newProxyInstance(loader, new Class[]{IGamePlayerAction.class}, handler);        //代理开始工作        proxy.login();        proxy.killBoss();        proxy.upgrade();    }}运行结果:玩家neil登录玩家neil打BOSS玩家neil升级

场景类main方法中,我们使用Proxy.newProxyInstance(loader, new Class[]{IGamePlayerAction.class}, handler);方法创建了一个代理类,该方法有三个参数:
1、ClassLoader(类加载器),被代理对象的类加载器,用来在运行时加载被代理对象的class文件;
2、new Class:代理类需要实现的接口;
3、InvocationHandler(实际处理器):实际的处理器,虽然在运行时产生了代理,但是仅仅是空实现了接口的方法,内部没有逻辑代码。代理去执行方法时,JVM会定向到其相关的Invocation对象的invoke方法,并将要执行的方法、参数、代理类传过去。
使用动态代理,我们不用创建代理类去实现被代理抽象接口了,完全交给JVM在运行时处理即可。我们要做的就是去关注InvocationHandler的实现。
JDK动态代理只能够通过实现被代理类的接口来代理进行。如果需要直接代理一个类,JDK动态代理就没有办法了,而CGLib动态代理就能够解决这个问题。

CGLib动态代理

CGLib(CodeGeneration Library代码生产库)是一个在运行期通过集成java类、实现java接口来生成子类的工具库。其底层是一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。
我们继续游戏案例来学习CGLib动态代理的使用,其核心由Enhancer类和MethodInterceptor接口及Callback接口相关集成体系相关类。
定义一个处理类实现MethodInterceptor接口:

package com.zz.aop.CGLibProxy;import java.lang.reflect.Method;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;/** * 实现MethodInterceptor接口,定义方法的拦截器  * @author neil * */public class MyMethodInterceptor implements MethodInterceptor {    @Override    public Object intercept(Object obj, Method method, Object[] args,            MethodProxy methodProxy) throws Throwable {        //System.out.println("登录");        // 调用目标方法,用methodProxy,而不是原始的method,以提高性能         Object result = methodProxy.invokeSuper(obj, args);        return result;    }}

使用Enhancer创建代理,封装成ProxyFactory工具类:

package com.zz.aop.CGLibProxy;import net.sf.cglib.proxy.Enhancer;public class ProxyFactory {    /**     * 通过CGLib获取代理     * @param superClass     * @return     */    public static Object createProxy(Class superClass){        Enhancer  enhancer  = new Enhancer();        //设置被代理类        enhancer.setSuperclass(superClass);        //被代理类加载器        enhancer.setClassLoader(superClass.getClassLoader());        //回调处理器        MyMethodInterceptor  interceptor = new MyMethodInterceptor();//      设置回调处理器        enhancer.setCallback(interceptor);        return  enhancer.create(new Class[]{String.class}, new Object[]{"neil"});    }}

场景类中模拟游戏过程:

package com.zz.aop.CGLibProxy;public class Client {    public static void main(String[] args) {        GamePlayer gamePlayer = new GamePlayer("neil");        GamePlayer proxy = (GamePlayer)ProxyFactory.createProxy(gamePlayer.getClass());        proxy.login();        proxy.killBoss();        proxy.upgrade();    }}结果:玩家neil登录玩家neil打BOSS玩家neil升级

创建代理由Enhancer类完成,需要设置三个必要参数:
1、被代理类的类对象:由代理类继承的父类,被代理者。
2、被代理类的类加载器:用来加载被代理类信息。
3、回调处理器:代理类执行方法时最终都会定向到该处理器的intercept()方法。


总结

了解了JDK动态代理和CGLib动态代理的使用后,我们可以发现动态代理一些共性:
1、被代理对象:如JDK动态代理的IGamePlayerAction 、CGLib动态代理的GamePlayer.被代理对象时我们实际业务中会多处使用的,当我们在所有的被代理对象方法执行前后要做一种统一的处理时,不可能去直接挨个其修改业务逻辑。这是我们就可以将该对象作为被代理对象,将方法前后的统一代理集中到代理类中实现。
2、代理对象:动态代理的代理类只是一个简单的占位对象,由代理类生成器生成。其执行方法最终都会定向到关联的处理器对象中的拦截方法上。
3、代理类生成器:顾名思义,是用来生成代理类的类,它需要被代理类的类信息和被代理对象以及相关联的处理器作为条件。
4、处理器:会和代理类关联,代理对象执行目标对象的方法时,自动定向到处理器的处理方法,如JDK动态代理的InvocationHandler.invoke()/CGLib动态代理的MethodInterceptor.intercept()方法。我们只要实现该接口方法,就可以编写自己的增强逻辑代码。

public Object intercept(Object obj, Method method, Object[] args,            MethodProxy methodProxy) throws Throwable {        Object result;        try {            System.out.println("目标方法执行前的增强逻辑");            result = methodProxy.invokeSuper(obj, args);            System.out.println("目标方法执行后的增强逻辑");            return result;        } catch (Exception e) {            System.out.println("抛出异常的增强逻辑");            throw e;        }finally{            System.out.println("目标方法执行完毕的增强逻辑(不管结果是正常返回还是异常)");        }    }