• 【RPC】动态代理及源码分析


    1. 动态代理简介

    代理模式是一种结构型设计模式,它的本质是对某个对象提供代理对象,让调用方仅与代理对象交互,无须感知真实的被代理对象。他的目的是控制对被代理对象的访问,解决直接访问对象时带来的问题。

    代理模式有三个角色

    • 抽象主题角色(subject):它既可以是一个抽象类,也可以是一个接口,它的职责是抽象和定义普通的业务行为。
    • 代理主题角色(Proxy):该角色也被称为代理类,它代理了真实主题角色的行为。
    • 真实主题角色(RealSubject):该角色也被称为被代理类,它实现了真实的业务行为,代理类代理的行为是由该角色真实执行的。

    在这里插入图片描述

    在使用代理模式时,自行编写代理代理类的方式叫做静态代理,在Java文件完成编译后,自行编写的代理类会生成实实在在的class文件。静态代理通常用于对原有业务逻辑的扩充,让被代理类专注于自己的功能而不会被业务逻辑的扩充影响。它也满足了软件开发的开闭原则,即对扩展开发、对修改关闭。

    静态代理有三个优点

    • 1)静态代理让真实主题角色的职责变得更加清晰。真实主题角色只需要实现实际的业务逻辑,不用关心别的业务逻辑,这样能让真实主题角色的职责更加清晰。
    • 2)提高了扩展性。需求不断迭代,如果需要在同一个业务逻辑中扩展功能,则可以在代理类中添加相关的功能,无须修改真实主题角色的逻辑。
    • 3)解耦。静态代理可以让调用者只感知代理主题角色的存在,对于真实主题角色时无感知的。

    静态代理的缺点 也很明显:

    • 1)真实主题角色和代理主题角色有许多冗余的代码,因为它们都实现了抽象主题对象。
    • 2)一个代理主题角色只服务于一个真实主题角色,随着主题角色的数量增加,代理主题角色的数量也随之增加,代理主题角色的数量也随之增加,增加了静态代理实现的烦琐度。
    • 3)需要在编译之前就确定哪个代理主题角色服务哪个真实主题角色,而无法在其他阶段才确定(比如运行时)。

    那我们是否可以通过其他方法生成代理主题角色呢?——动态代理就解决了这个问题。

    动态代理从字面上就很好理解,就是动态生成代理类。动态生成动态代理类是由JVM的类加载机制决定的。我们都知道Java虚拟机类加载过程主要分为五个阶段:加载、验证、准备、解析、初始化。在加载阶段会通过一个类的全限定名来获取定义此类的二进制字节流,而二进制字节流的来源有很多种,比如从Zip包获取、从网络传输流中获取及在运行时通过计算生成等。动态代理技术就是通过运行时计算生成代理类,并且从该生成的代理类中读取二进制字节流,用于类加载。

    动态代理在RPC中的应用

    RPC的本地存根就是通过动态代理实现的。 本地存根最重要的作用就是让远程调用像本地调用一样直接进行函数调用,无须关心地址空间隔离、函数不匹配等问题。本地存根让调用者不需要感知是如何发生RPC调用的,它屏蔽了下游的编解码、序列化、网络通道等一切细节,让调用者认为只是发起了一次本地函数调用。从这一点来看,本地存根的作用与代理主题角色的作用非常相似,代理主题角色起到了让上游调用者无须感知下游细节的作用。

    想一下,为什么RPC的本地存根为什么要采用动态代理而不用静态代理呢?

    在RPC中,代理类代理的是下游服务的接口,如果使用静态代理,那么在编译前就必须编写代理类,在provide端有多少个服务接口,就必须在consumer端实现多少个代理类,而且这些代理类实现的逻辑都是一摸一样的,都是执行编/解码、序列化和网络通信等操作,只有代理的服务接口不一样,导致出现非常多的冗余代码。

    了解了动态代理在RPC领域中的重要性,那么动态代理该如何实现呢?

    因为需要在运行时生成对应的代理类,所以实现动态代理类就存在最关键的三个问题:

    • 如何读取被代理类的元数据信息?
    • 如何根据获取的类信息生成对应的代理类字节码?
    • 如何实例化代理类、创建代理类对象?

    只要解决了这三个关键问题,也就实现了动态代理。其中第一个问题中的被代理类可能是接口,也可能不是接口。JDK自带的动态代理方案只能代理接口,而CGLib可以代理所有的类。但是在RPC领域中,服务基本上是通过接口暴露的,动态代理只需要代理接口即可。第二个问题是最重要的一点,因为动态代理与静态代理最大的区别就是可以在运行时生成代理类。第三个问题中的实例化代理类和创建类对象大部分都是通过反射技术获取构造函数实现的。另外,动态还做了许多优化和性能上的提升,比如一些缓存设计等。

    2. JDK自带的动态代理方法

    Java的JDK从1.3版本开始就开始支持动态代理技术。JDK动态代理是JDK为了解决静态代理中生成大量的代理类造成的冗余问题。JDL动态代理只支持对接口的代理,主要通过 java.lang.reflect.Proxy类和 java.lang.reflect.InvocationHandler接口实现动态代理。

    • java.lang.reflect.Proxy:可以看作一个生成代理对象的工具类,它封装了生成动态代理类及创建代理类对象的逻辑。
    • java.lang.reflect.InvocationHandler接口:它定义了一个invoke方法,该方法完成了调用真实主题类的对象方法。JDK动态代理根据被代理的接口生成所有的方法,当生成动态代理类后,所有调用接口的方法都必须先通过InvocationHandlerinvoke方法,统一由invoke方法进行真实方法调用,这样做就可以在不改变已有代码结构的情况下增强或者控制对象的行为。

    通过JDK实现动态代理的原理

    JDK自带的动态代理方案最关键的就是Proxy的newProxyInstance方法,该方法直接返回了一个代理对象。也就是说,在该方法中实现了读取需要代理的服务接口的元数据信息,根据获取的类信息生成对应的代理类字节码和实例化代理类,以及创建类对象的目的。下面就这三个关键的步骤来剖析JDK动态代理的原理(JDK版本为1.8)。

    newProxyInstance是Proxy的静态方法,以下是它的源代码及部分重要步骤的注释:

    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
    {
        Objects.requireNonNull(h);
    
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
    		// 1. 前置检查
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
    
        /*
         * Look up or generate the designated proxy class.
    	 * 2. 查找或者生成代理类
         */
        Class<?> cl = getProxyClass0(loader, intfs);
    
        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
    		// 3. 通过反射获得构造函数
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
    		// 4. 生成代理类的实例并把InvocationHandlerImpl的实例传给它的构造方法
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    从该方法的源代码中可以看出,该方法主要分为四个部分:

    • 1)前置检查
    • 2)查找或者生成代理类
    • 3)通过反射获得构造函数
    • 4)实例化代理类,创建代理类的对象

    第一步前置检查是对参数合法性的校验。 在生成代理类之前,如果启用了安全管理器,则需要先进行前置检查,也就是调用checkProxyAccess方法。主要检查的是一下几点:

    • 检查该接口对应的类加载器是否为空,也就是参数loader是否等于null。
    • 检查调用此方法的方法调用者的类的类加载是否为空。
    • 检查调用线程是否具有执行操作的权限。
    • 检查接口类的包是否满足当前调用者的ClassLoader的访问权限。

    第二步是查找或者生成代理类。 在生成代理类之前,首先会查找缓存,是否已经生成该接口的代理类,如果没有缓存命中,则会通过ProxyClassFactory的apply方法创建代理类。这里注重讲解ProxyClassFactory是如何创建代理类的。

    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names
    	// 所有代理类名称的前缀
        private static final String proxyClassNamePrefix = "$Proxy";
    
        // next number to use for generation of unique proxy class names
    	// 下一个用于生成唯一代理类名称的数字
        private static final AtomicLong nextUniqueNumber = new AtomicLong();
    
        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
    
            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
    			// 验证类加载器是否将此接口的名称解析为相同的Class对象
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
    			 * 验证是不是一个接口
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
    			 * 验证这个接口是否重复生成代理类
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }
    
            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
    
            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
    		 * 记录非公共代理接口的包
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }
    
            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
    			// 如果没有非公共代理接口,则使用com.sum.proxy包名
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
    
            /*
             * Choose a name for the proxy class to generate.
    		 * 生成代理类名称
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
            /*
             * Generate the specified proxy class.
    		 * 生成代理类二进制数据
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
    			// 将二进制数据转化为Class对象
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109

    从上面的代码也可以看出,JDK动态代理只支持接口代理,如果不是接口,则抛出异常。ProxyClassFactory的apply方法做了一些准备工作,比如类名、包名的生成等,而生成代理类二进制数据的逻辑被封装在ProxyGenerator中,对应的方法是generateProxyClass。将二进制数据转化为Class对象的逻辑是调用了native方法的defineClass()。

    第三步就是通过反射获得构造函数。 JDK自带的动态代理方案中实例化代理类是通过反射获取构造函数实现的。

    第四步就是创建代理类的对象,把InvocationHandler的实现类的实例对象传给代理类的构造方法。

    3. CGLib动态代理方案

    CGLib(Code Generation Library)是一个开源的代码生成库,它提供了动态代理等技术支持。它被运用在许多场景中,比如Hibernate使用它来满足动态生成持久化对象的字节码的需求,Spring AOP 中也可以选择使用CGLib作为动态代理的实现方案。CGLib采用ASM字节码生成框架,使用字节码技术生成代理类。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib的原理是动态生成被代理类的子类。

    CGLib中与动态代理最相关的就是Enhance类和MethodInterceptor接口。

    • Enhancer类:从类命名就可以看出该类的作用就是功能的增强,它是CGLib中的一个字节码增强器,方便扩展需要处理的类,是CGLib实现动态代理的关键类。它的主要功能就是在运行时为指定的类创建一个代理对象。
    • MethodInterceptor接口:从接口名称的命名就可以看出,MethodInterceptor接口就是一个方法蓝机器,MethodInterceptor接口的作用与JDK中的InvocationHandler类似,都是提供执行调用真实主题的方法。使用者可以在调用真是主题的方法前/后添加自己相关的逻辑,实现对被代理类的方法控制及功能的增强。

    使用CGLib实现动态代理的原理

    使用CGLib实现动态代理非常简单,只要实现MethodInterceptor接口及调用Enhancer中的一些方法即可。下面我们就从Enhancer的create方法的源码开始讲解。(这里以无参的情况举例)(源码版本为cglib-3.3.0)

    // 使用目标类中的无参构造方法
    public Object create() {
        // 将classOnly设置为false,代表既要生成代理类,又要创建代理类对象
    	// 代表是否要创建对象
    	classOnly = false;
    	// 把参数类型数组设置为空
    	// 代表参数类型
        argumentTypes = null;
        return createHelper();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    下面是Enhancer的createHelper方法的源码:

    private Object createHelper() {
    	// 1. 校验
        preValidate();
        // 2. 生成唯一key
    	Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
                ReflectUtils.getNames(interfaces),
                filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
                callbackTypes,
                useFactory,
                interceptDuringConstruction,
                serialVersionUID);
        this.currentKey = key;
    	// 调用父类的create方法
        Object result = super.create(key);
        return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    该方法主要执行了以下三个步骤:

    • 1)校验:对预声明的回调方法进行验证,如果又多个回调,则必须存在作为调度器的CallbackFilter。
    • 2)生成唯一key:构建该类增强操作唯一的key。
    • 3)调用父类AbstractClassGenerator的create方法。

    从上述源码可以看出,关键的逻辑发生在AbstractClassGenerator的create方法中,下面就是create方法的源码:

    protected Object create(Object key) {
        try {
    		// 1. 获取用于加载生成类的类加载器
            ClassLoader loader = getClassLoader();
            Map<ClassLoader, ClassLoaderData> cache = CACHE;
    		// 2. 从缓存中获取这个类加载器加载过的数据
            ClassLoaderData data = cache.get(loader);
    		// 3. 如果没有加载过数据,则同步创建该类加载器的ClassLoaderData对象,并且加入缓存
            if (data == null) {
                synchronized (AbstractClassGenerator.class) {
                    cache = CACHE;
                    data = cache.get(loader);
                    if (data == null) {
                        Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
                        data = new ClassLoaderData(loader);
                        newCache.put(loader, data);
                        CACHE = newCache;
                    }
                }
            }
            this.key = key;
    		// 4. 获取对象,这里获取的对象如果没是Class对象,则代表没有实例化过该类
            Object obj = data.get(this, getUseCache());
            if (obj instanceof Class) {
    			// 如果是第一次进行实例化操作,则利用反射进行实例化
                return firstInstance((Class) obj);
            }
    		// 如果不是初次实例化,则从ClassLoaderData中的得到之前维护的内容
            return nextInstance(obj);
        } catch (RuntimeException e) {
            throw e;
        } catch (Error e) {
            throw e;
        } catch (Exception e) {
            throw new CodeGenerationException(e);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    该方法的逻辑基本都用注释体现出来了,其中比较关键的点:

    • 如果需要代理的类已经被代理过,也就是生成代理类,那么可以从该代理类对应的ClassLoaderData对象中获取已经实例化的对象,也就是AbstractClassGenerator中的内部类ClassLoaderData存放了类加载器加载过的实例对象。
    • 如果没有进行过实例化,则第一次实例化是通过反射实现的,也就是调用fistInstance方法,反射使用的是java.lang.reflect类库,但是在第一次实例化时,会缓存反射过程中关键的实例,比如Constructor对象等,具体的缓存实现使用EnhancerFactoryData类作为缓存的数据结构,这里就不再展开。由于这些反射过程中的实例在第一次实例化时被缓存了,所以当第二次实例化,也就调用nextInstance方法时,可以直接用缓存中数据实现代理类的实例化,比JDK的实例化效率高。
    • 在第三步中,如果缓存中没有对应的ClassLoaderData对象,那么会创建一个在ClassLoaderData对象,在ClassLoaderData构造函数中由后置调用的函数实现,用于懒加载。
    • 在第四步中调用了ClassLoaderData的get方法,如果是第一次生成该代理类,则该方法返回的是代理类的Class对象,否则返回的是ClassLoaderData对象。

    讲到这里其实代理类对象已经生成了,但是并没有说代理类的字节码是如何实现的,所以下面主要讲解如何让生成代理类的字节码。 根据上述的介绍,生成代理类字节码有两个入口都调用了AbstractClassGenerator的generate方法,分别是ClassLoaderData构造方法,以及第四步中调用的ClassLoaderData的get方法。

    第一个入口就是ClassLoaderData构造函数,下面就是ClassLoaderData构造函数的源码:

    public ClassLoaderData(ClassLoader classLoader) {
        if (classLoader == null) {
            throw new IllegalArgumentException("classLoader == null is not yet supported");
        }
        this.classLoader = new WeakReference<ClassLoader>(classLoader);
    	// 定义一个生成字节码的函数,用于懒加载
        Function<AbstractClassGenerator, Object> load =
                new Function<AbstractClassGenerator, Object>() {
                    public Object apply(AbstractClassGenerator gen) {
    					// 生成代理类Class对象
                        Class klass = gen.generate(ClassLoaderData.this);
                        return gen.wrapCachedClass(klass);
                    }
                };
    	// 将函数加入缓存
        generatedClasses = new LoadingCache<AbstractClassGenerator, Object, Object>(GET_KEY, load);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在ClassLoaderData构造函数中并没有执行AbstractClassGenerator的generate方法,只是把它作为一个函数缓存起来,触发该方法被调用的逻辑则是在ClassLoaderData的get方法中,所以还是得从ClassLoaderData的get方法的源码:

    public Object get(AbstractClassGenerator gen, boolean useCache) {
        if (!useCache) {
    		// 如果不使用缓存,则直接生成代理类
            return gen.generate(ClassLoaderData.this);
        } else {
    		// 从缓存中获取,如果不存在,则生成代理类
            Object cachedValue = generatedClasses.get(gen);
            return gen.unwrapCachedValue(cachedValue);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这里有两个逻辑,useCache代表是否使用缓存,该配置可以直接通过Enhancer的setUseCache()来配置,默认使用缓存。generatedClasses是一个LoadingCache对象,也就是ClassLoaderData构造函数中的generatedClasses,这里的generatedClasses.get其实就是调用了LoadingCache的get方法。下面是LoadingCache的get方法的源码:

    public V get(K key) {
    	// 获得缓存的key
        final KK cacheKey = keyMapper.apply(key);
        Object v = map.get(cacheKey);
    	// 如果从缓存中取出的对象是FutureTask类型,则说明代理类还在创建中
    	// 如果不是FutureTask类型,则说明已经创建代理类,可直接返回
        if (v != null && !(v instanceof FutureTask)) {
            return (V) v;
        }
    	// 如果没有对该key建立过FutureTask,那么创建该任务
        return createEntry(key, cacheKey, v);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    从LoadingCache的get方法的源码可以看出,LoadingCache的createEntry方法在最后被执行,以下就是createEntry方法的源码:

    protected V createEntry(final K key, KK cacheKey, Object v) {
        FutureTask<V> task;
    	// 标记为一个新建的流程
        boolean creator = false;
    	// 再次检测v是否为空(因为是异步的),如果不为空,则直接返回FutureTask
        if (v != null) {
            // Another thread is already loading an instance
            task = (FutureTask<V>) v;
        } else {
    		// 否则创建FutureTask,该任务用来调用apply方法
            task = new FutureTask<V>(new Callable<V>() {
                public V call() throws Exception {
                    return loader.apply(key);
                }
            });
    		// 加入集合
            Object prevTask = map.putIfAbsent(cacheKey, task);
            if (prevTask == null) {
                // creator does the load
    			// 如果没有值
                creator = true;
                task.run();
            } else if (prevTask instanceof FutureTask) {
    			// prevTask不为空并且是FutureTask类型,则说明有线程在执行putIfAbsent之前
    			// 已经创建任务了,那就把该线程新建的task作为当前的任务
                task = (FutureTask<V>) prevTask;
            } else {
    			// 否则返回新的任务FutureTask
                return (V) prevTask;
            }
        }
    
        V result;
        try {
    		// 任务执行完后获取执行结果
            result = task.get();
        } catch (InterruptedException e) {
            throw new IllegalStateException("Interrupted while loading cache item", e);
        } catch (ExecutionException e) {
            Throwable cause = e.getCause();
            if (cause instanceof RuntimeException) {
                throw ((RuntimeException) cause);
            }
            throw new IllegalStateException("Unable to load cache item", cause);
        }
        if (creator) {
    		// 放入缓存
            map.put(cacheKey, result);
        }
        return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    可以看到这里创建了FutureTask来执行在ClassLoaderData构造函数中缓存的生成字节码的函数,也就是调用了apply方法,此时就是执行了代理类的生成逻辑。

    AbstractClassGenerator的generate方法的两个调用入口都介绍完了,接下来就是如何生成字节码,也就是调用AbstractClassGenerator的generate方法。下面就是该方法的源码:

    protected Class generate(ClassLoaderData data) {
        Class gen;
        Object save = CURRENT.get();
        CURRENT.set(this);
        try {
    		// 获取用于加载代理类的ClassLoader
            ClassLoader classLoader = data.getClassLoader();
            if (classLoader == null) {
                throw new IllegalStateException("ClassLoader is null while trying to define class " +
                        getClassName() + ". It seems that the loader has been expired from a weak reference somehow. " +
                        "Please file an issue at cglib's issue tracker.");
            }
    		// 生成代理类的类名
            synchronized (classLoader) {
                String name = generateClassName(data.getUniqueNamePredicate());
                data.reserveName(name);
                this.setClassName(name);
            }
            if (attemptLoad) {
                try {
                    gen = classLoader.loadClass(getClassName());
                    return gen;
                } catch (ClassNotFoundException e) {
                    // ignore
                }
            }
    		// 通过ASM生成代理类的字节码字节数组数据
            byte[] b = strategy.generate(this);
    		// 通过字节码字节数组数据获取代理类的className
            String className = ClassNameReader.getClassName(new ClassReader(b));
            ProtectionDomain protectionDomain = getProtectionDomain();
            synchronized (classLoader) { // just in case
                if (protectionDomain == null) {
    				// 将代理类的字节码字节数组数据转化为代理类的Class类型对象
                    gen = ReflectUtils.defineClass(className, b, classLoader);
                } else {
                    gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);
                }
            }
    		// 返回代理类
            return gen;
        } catch (RuntimeException e) {
            throw e;
        } catch (Error e) {
            throw e;
        } catch (Exception e) {
            throw new CodeGenerationException(e);
        } finally {
            CURRENT.set(save);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    该方法大致可以分为以下四个步骤:

    • 1)获取代理类的类加载器
    • 2)生成代理类类名
    • 3)通过ASM生成代理类的字节码字节数组数据
    • 4)将代理类的字节码字节数组数据转化为代理类的Class类型对象。

    至此,CGLib生成代理类对象的过程就全部介绍完了。从上述的源码可以看到,CGLib实现了许多缓存策略,用于提升动态代理的性能。这里没有讲解如何通过ASM生成代理类的字节码字节数组数据,因为它生成Class文件的操作和ASM库的ClassVisitor访问Class文件的流程基本类似,具体的实现细节可以参考Enhancer的generateClass方法。从CGLib动态代理的实现原理可以看出,它与JDK动态代理的区别是CGLib底层的字节码生成技术采用的是ASM,并且做了非常多的优化。

  • 相关阅读:
    MongoDB副本集介绍与部署
    C语言求一维数组循环左移一位
    Linux 部署 MinIO 分布式对象存储 & 配置为 typora 图床
    低风险稳健套利策略
    【KCC@南京】KCC南京"数字经济-开源行"
    SpringCloud 学习笔记(2 / 3)
    25期代码随想录算法训练营第一天 | 704. 二分查找,27. 移除元素
    Stable Diffusion扩散模型推导公式的基础知识
    Java XSSFWorkbook 常用表格操作
    计算机图像处理-直方图均衡化
  • 原文地址:https://blog.csdn.net/Aibiabcheng/article/details/125500230