Cglib和jdk动态代理的区别

Cglib和jdk动态代理的区别(转载)

原文地址:https://www.cnblogs.com/sandaman2019/p/12636727.html

  • 动态代理解决了方法之间的紧耦合,在方法调用方法中间可动态进行附加操作
  • IOC解决了类与类之间的紧耦合!

Cglib 和 jdk 动态代理的区别?

  1. Jdk动态代理:利用拦截器(必须实现 InvocationHandler )加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理
  2. Cglib动态代理:利用 ASM 框架,对代理对象类生成的 class 文件加载进来,通过修改其字节码生成子类来处理

什么时候用 cglib 什么时候用 JDK 动态代理?

  1. 目标对象生成了接口 默认用 JDK 动态代理
  2. 如果目标对象使用了接口,可以强制使用 cglib
  3. 如果目标对象没有实现接口,必须采用 cglib 库, Spring 会自动在 JDK 动态代理和 cglib 之间转换

JDK 动态代理和 cglib 字节码生成的区别?

  1. JDK 动态代理只能对实现了接口的类生成代理,而不能针对类
  2. Cglib 是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法的增强,但是因为采用的是继承,所以该类或方法最好不要设置为 final ,对于 final 类或方法,是无法继承的

Cglib 比 JDK 快?

  1. cglib 底层是 ASM 字节码生成框架,但是字节码技术生成代理类,在 JDK1.6 之前比使用 java 反射的效率要高
  2. 在 JDK1.6 之后逐步对 JDK 动态代理进行了优化,在调用次数比较少时效率高于 cglib 代理效率
  3. 只有在大量调用的时候 cglib 的效率高,但是在 JDK8 的时候JDK的效率已高于 cglib
  4. Cglib 不能对声明 final 的方法进行代理,因为 cglib 是动态生成代理对象,final 关键字修饰的类不可变只能被引用不能被修改

Spring 如何选择是用 JDK 还是 cglib?

  1. 当 bean 实现接口时,会用 JDK 代理模式
  2. 当 bean 没有实现接口,用 cglib实现
  3. 可以强制使用cglib(在spring配置中加入<aop:aspectj-autoproxy proxyt-target-class="true"/>)

Cglib 原理

动态生成一个要代理的子类,子类重写要代理的类的所有不是 final 的方法。在子类中采用方法拦截技术拦截所有的父类方法的调用,顺势织入横切逻辑,它比 Java 反射的 jdk 动态代理要快
Cglib 是一个强大的、高性能的代码生成包,它被广泛应用在许多 AOP 框架中,为他们提供方法的拦截。
最底层的是字节码 Bytecode ,字节码是 java 为了保证依次运行,可以跨平台使用的一种虚拟指令格式
在字节码文件之上的是ASM,只是一种直接操作字节码的框架,应用 ASM 需要对 Java 字节码、 class 结构比较熟悉
位于 ASM 上面的是 Cglib、groovy、beanshell,后来那个种并不是 Java 体系中的内容是脚本语言,他们通过 ASM 框架生成字节码变相执行 Java 代码,在JVM中程序执行不一定非要写 java 代码,只要能生成 java 字节码, JVM 并不关系字节码的来源
位于 cglib、groovy、beanshell之上的就是 hibernate 和 Spring AOP,最上面的是applications,既具体应用,一般是一个web项目或者本地跑一个程序、
使用 cglib 代码对类做代理?
使用 cglib 定义不同的拦截策略?
构造函数不拦截方法
用 MethodInterceptor 和 Enhancer 一个动态代理

案例

Cglib 的第三方库提供的动态代理

/**
 * 动态代理:
 *  特点:字节码随用随创建,随用随加载
 *  作用:不修改源码的基础上对方法增强
 *  分类:
 *      基于接口的动态代理
 *      基于子类的动态代理
 *  基于子类的动态代理:
 *      涉及的类:Enhancer
 *      提供者:第三方cglib库
 *  如何创建代理对象:
 *      使用Enhancer类中的create方法
 *  创建代理对象的要求:
 *      被代理类不能是最终类
 *  newProxyInstance方法的参数:在使用代理时需要转换成指定的对象
 *      ClassLoader:类加载器
 *          他是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法
 *      Callback:用于提供增强的代码
 *          他是让我们写如何代理。我们一般写一个该接口的实现类,通常情况加都是匿名内部类,但不是必须的。
 *          此接口的实现类,是谁用谁写。
 *          我们一般写的都是该接口的子接口实现类,MethodInterceptor
 */
com.dynamic.cglib.Producer cglibProducer= (com.dynamic.cglib.Producer) Enhancer.create(
        com.dynamic.cglib.Producer.class,
        new MethodInterceptor() {
            /**
             *  执行被代理对象的任何方法都会经过该方法
             * @param obj
             * @param method
             * @param args
             *      以上三个参数和基于接口的动态代理中invoke方法的参数是一样的
             * @param proxy:当前执行方法的代理对象
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                Object returnValue=null;
                Float money=(Float)args[0];
                if("saleProduct".equals(method.getName())){
                   returnValue= method.invoke(producer,money*0.8f);
                }
                return returnValue;
            }
        }
);
cglibProducer.saleProduct(100.0f);

JDK 本身提供的动态代理实现

    /**
     * 动态代理:
     *  特点:字节码随用随创建,随用随加载
     *  作用:不修改源码的基础上对方法增强
     *  分类:
     *      基于接口的动态代理
     *      基于子类的动态代理
     *  基于接口的动态代理:
     *      涉及的类:proxy
     *      提供者:Jdk官方
     *  如何创建代理对象:
     *      使用Proxy类中的newProxyInstance方法
     *  创建代理对象的要求:
     *      被代理类最少实现一个接口,如果没有则不能使用
     *  newProxyInstance方法的参数:在使用代理时需要转换成指定的对象
     *      ClassLoader:类加载器
     *          他是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法
     *      Class[]:字节码数组
     *          它是用于让代理对象和被代理对象有相同方法。固定写法
     *      InvocationHandler:用于提供增强的代码
     *          他是让我们写如何代理。我们一般写一个该接口的实现类,通常情况加都是匿名内部类,但不是必须的。
     *          此接口的实现类,是谁用谁写。
     */
    IProducer proxyProducer=  (IProducer) Proxy.newProxyInstance(
            producer.getClass().getClassLoader(),
            producer.getClass().getInterfaces(),
            new InvocationHandler() {
                /**
                 * 作用:执行被代理对象的任何接口方法都会经过该方法
                 * @param proxy  代理对象的引用
                 * @param method 当前执行的方法
                 * @param args   当前执行方法所需的参数
                 * @return       和被代理对象有相同返回值
                 * @throws Throwable
                 */
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 提供增强的代码
                    // 1、获取方法执行的参数
                    Object returnValue=null;
                    Float money=(Float)args[0];
                    if("saleProduct".equals(method.getName())){
                        returnValue= method.invoke(producer,money*0.8f);
                    }
                    return returnValue;
                }
            }
    );

总结

Jdk 中的动态代理

JDK 中的动态代理是通过反射类 Proxy 以及 InvocationHandler 回调接口实现的,但是 JDK 中所有要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中有一定的局限性,而且使用反射的效率也不高

Cglib 实现

使用 cglib 是实现动态代理,不受代理类必须实现接口的限制,因为 cglib 底层是用 ASM 框架,使用字节码技术生成代理类,你使用 Java 反射的效率要高, cglib 不能对声明 final 的方法进行代理,因为 cglib 原理是动态生成被代理类的子类

Cglib JDK
是否提供子类代理
是否提供接口代理 是(可强制)
区别 必须依赖于 CGLib 的类库,但是它需要类来实现任何接口代理的是指定的类生成一个子类,覆盖其中的方法 通过 InvocationHandler 使用 Proxy.newProxyInstance 产生代理对象;被代理的对象必须要实现接口

原文地址:https://www.cnblogs.com/sandaman2019/p/12636727.html

posted @ 2022-01-20 10:57  临渊不羡渔  阅读(986)  评论(0编辑  收藏  举报