动态代理

作者: 雪驹 | 来源:发表于2017-03-07 23:00 被阅读0次

    动态代理可以在不改动原代码的前提下,为其添加验权、事务控制、日志打印等功能。

    例如,统计某方法运行所消耗的时间。通常做法如下:

    // 记录此方法的开始时间(动态代理模式下,这部分不在此方法中实现)
    // 处理业务
    // 记录此方法的结束时间并打印(动态代理模式下,这部分不在此方法中实现)
    

    在动态代理里,时间统计代码写在了代理模块里。将业务代码和时间统计代码完全解耦了。

    动态代理有两种实现方式,一种是调用 JDK。另一种是使用 Cglib,两者的区别是Cglib是可以不需要接口。

    就上面伪代码的例子。如果用代理模式,应该如何做呢?下面就是我用代码完成的一个示例及相关说明。

    用调用JDK的方式实现动态代理,有这么几个类:
    1、JDK 实现的 Proxy 类。此类的 newProxyInstance 方法用来生成代理对象。需要 InvocationHandler 的实例和目标代码类的字节码作为参数。
    2、实现 InvocationHandler 接口的类,这个类通过实现InvocationHandler 接口的 invoke 方法,将「代理的代码」注入到「目标代码」中。而 createProxyIntance 方法用目标对象字节码和 handler(此类)以反射的方式生成代理对象。

    public class RemindHandler implements InvocationHandler {
        
        /** 要注入的目标对象 */
        private Object target;
        
        public Object createProxyIntance(Object target) {
            this.target = target;
            Class<?> clazz = this.target.getClass();// 目标对象的字节码
            
            // 通过目标对象字节码和 handler(此类)以反射的方式生成代理对象。
            Object object = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
            return object;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("学习之前,提醒自己做好学习计划。");
            method.invoke(target);
            System.out.println("学习之后,提醒自己及时总结,做笔记,自测。");
            return null;
        }
    }
    

    3、目标代码类,对目标代码的具体事务进行实现。

    public class StudyEnglish implements Study{
    
        @Override
        public void startReading() {
            System.out.println("阅读英语教材中...");
        }
    
    }
    

    4、目标代码类的接口类。

    public interface Study {
        public void startReading();
    }
    

    下面是对以上代码进行测试的代码

    public static void main(String args[]){
            StudyEnglish study = new StudyEnglish();
            RemindHandler handler = new RemindHandler();
            Study s = (Study) handler.createProxyIntance(study);
            s.startReading();
        }
    }
    

    一句话总结就是,动态代理通过被代理类的字节码和注入的代码(日志,权限,事务),以反射的方式生成一个代理的对象。

    备注1:代理模式中,日志、验权、事务等代码是可复用的,不变的代码,在项目中大部分属于技术代码。而被代理的代码,大部分属于变化的业务代码。

    备注2:动态代理需要注意的一个点
    Spring事务处理时自我调用的解决方案及一些实现方式的风险

    相关文章

      网友评论

        本文标题:动态代理

        本文链接:https://www.haomeiwen.com/subject/qgevgttx.html