zoukankan      html  css  js  c++  java
  • Cglib动态代理浅析

    原文同步发表至个人博客【夜月归途】

    原文链接:http://www.guitu18.com/se/java/2018-06-29/18.html

    本博客关于Java动态代理相关内容直达链接:

    1. JDK动态代理浅析
    2. Cglib动态代理浅析
    3. JDK动态代理深入理解分析并手写简易JDK动态代理(上)
    4. JDK动态代理深入理解分析并手写简易JDK动态代理(下)

    Java中的动态代理设计模式是非常经典且非常重要的设计模式之一,在感叹设计者的天才设计至于,我们想去探究一下这个设计模式是如何来实现的;

    著名的spring框架的AOP的原理就是Java的动态代理机制;

    在Spring中动态代理是实现有两种:JDK动态代理和Cglib动态代理,本篇分析的是Cglib动态代理的实现;

    JDK动态代理之前已经做过分析了[ JDK动态代理浅析 ],通过案例我们知道了,Java动态代理的强大之处;但是不难看出来使用JDK动态代理也有着它的局限性,JDK动态代理是在JVM内部动态的生成class字节码对象,但是JDK动态代理只能针对接口进行操作,也就是只能对接口的实现类去进行代理;

    现在分析一下另一种常用的动态代理技术:Cglib动态代理;

    CGLIB(Code Generation Library)是一个开源项目;
    是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口;
    CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类;
    Cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理;
    所以Cglib可以为无接口的类直接做代理,当然有接口的类也是可以的并无影响。

    因为采用的是继承,所以不能对final修饰的类进行代理;Cglib的使用限制无疑要比JDK动态代理要宽松很多;

    废话不多说,先上案例;

    这里说明一下,如果是Spring框架那么无所谓,Spring框架的spring-core.jar包中已经集成了cglib与asm;如果你要单独使用CGLIB,那么需要导入cglib的jar包和asm相关jar包;

    Programmer类:

    public class Programmer {
        public void work(String name) {
            System.out.println(name + " 正在工作...");
        }
    }

    CglibProxyFactory代理类:

    public class CglibProxyFactory implements MethodInterceptor {
        private Object target;
        public CglibProxyFactory(Object target) {
            this.target = target;
        }
        public Object getProxyInstance() {
            // 创建 Enhancer 对象
            Enhancer enhancer = new Enhancer();
            // 设置目标对象的Class
            enhancer.setSuperclass(target.getClass());
            // 设置回调操作,相当于InvocationHandler
            enhancer.setCallback(this);
            return enhancer.create();
        }
        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
                throws Throwable {
            System.out.println("开始事务...");
            // 两种方式执行方法,第二行注释掉的,和当前行代码效果相同,下面会分析;
            Object invoke = method.invoke(target, args);
            // Object invoke = methodProxy.invokeSuper(proxy, args);
            System.out.println("提交/回滚事务...");
            return invoke;
        }
    }

    Cglib创建代理的方式非常简单,是通过Enhancer对象,需要给它set两个参数:

    设置目标对象的Class; 设置回调操作,相当于InvocationHandler,需要实现MethodInterceptor 接口; 之后执行create()方法即可返回目标对象的代理对象;

    这里为了方便,直接让CglibProxyFactory实现了MethodInterceptor 接口,需要实现重写MethodInterceptor.intercept()方法;

    在intercept()方法中,其实跟InvocationHandler.invoke中做的事是一样的,我们需要调用method.invoke()去执行方法;

    在案例中,还有注释掉的一行:Object invoke = methodProxy.invokeSuper(proxy, args);

    这个和上面的 Object invoke = method.invoke(target, args); 是一样的效果,都是代理执行方法,下面进行说明;

    从代码中看出,intercept()的参数比JDK的invoke()多了一个,前三个参数跟JDK的invoke()方法一样不多说,第四个参数MethodProxy methodProxy是当前代理对象执行的方法的一个代理,也就是MethodProxy methodProxy是第二个参数Method method的代理;

    前面我们说过,Cglib动态代理的机制是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理;也就是参数一Object proxy实际上是我们的目标对象Object target的一个子类;

    我们直接使用 method.invoke(target, args) 执行方法,和使用methodProxy.invokeSuper(proxy, args) 执行是一个什么关系呢;

    第一个被代理类target直接调用method执行没问题,第二个其实是target的子类proxy去掉用父的方法,我第一次看这里也是有点绕哈,但是其实就是这么个关系,这两个方法执行的效果是一样的,但是通常我们只使用第一种方式;

    测试执行,贴测试代码:

    public class CglibProxyFactoryTest {
        public static void main(String[] args) {
            Programmer programmer = new Programmer();
            CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(programmer);
            Programmer instance = (Programmer) cglibProxyFactory.getProxyInstance();
            instance.work("夜月归途");
        }
    }

    控制台输出:

    开始事务... 
    夜月归途 正在工作... 
    提交/回滚事务... 
    

    看到这里和JDK的动态代理是一样的,Cglib的的动态代理没有接口的限制,有接口的类和无接口的类都可以被代理,只要不是final的;

    在这里,同样代码可以精简一下:

    public class CglibProxyFactory {
        public static Object getProxyInstance(Object target) {
            // 创建 Enhancer 对象
            Enhancer enhancer = new Enhancer();
            // 设置目标对象的Class
            enhancer.setSuperclass(target.getClass());
            // 设置回调操作,相当于InvocationHandler
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
                        throws Throwable {
                    System.out.println("开始事务...");
                    // Object invoke1 = method.invoke(target, args);
                    Object invoke2 = methodProxy.invokeSuper(proxy, args);
                    System.out.println("提交/回滚事务...");
                    return invoke2;
                }
            });
            return enhancer.create();
        }
    }

    以匿名内部类形式创建代理对象,这样子代码清爽许多;

    尾巴:

    1. 静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
    2. JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
    3. CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;
    4. 如果目标对象有接口,优先使用jdk动态代理,如果目标对象无接口,使用cglib动态代理,如:Spring框架的代理选择机制就是这样的;
  • 相关阅读:
    java学习--基础知识进阶第十一天--笔记
    java学习--基础知识进阶第十天--笔记
    java学习--基础知识进阶第十天--标准输入流 & 转换流 & 打印流、对象操作流 、Properties集合
    java学习--基础知识进阶第九天--笔记
    java学习--基础知识进阶第九天-- File类、字符流与字节流
    java学习--基础知识进阶第八天--笔记
    java学习--基础知识进阶第八天--异常体系&异常处理、Throwable常用方法&自定义异常、递归
    java学习--基础知识进阶第七天--笔记
    java学习--基础知识进阶第七天--HashSet集合、HashMap集合(集合遍历)
    java学习--基础知识进阶第六天--笔记
  • 原文地址:https://www.cnblogs.com/guitu18/p/10224643.html
Copyright © 2011-2022 走看看