设计模式【3.3】-- CGLIB动态代理源码解读

492 阅读5分钟
图片名称

cglib 动态代理

cglib介绍

CGLIB 是一个开源项目,一个强大高性能高质量的代码生成库,可以在运行期拓展 Java 类,实现 Java 接口等等。底层是使用一个小而快的字节码处理框架 ASM,从而转换字节码和生成新的类。

理论上我们也可以直接用 ASM 来直接生成代码,但是要求我们对 JVM 内部,class 文件格式,以及字节码的指令集都很熟悉。

这玩意不在 JDK 的包里面,需要自己下载导入或者 Maven 坐标导入。

我选择 Maven 导入, 加到 pom.xml 文件:

<dependencies>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
</dependencies>

Student.java:

public class Student {
    public void learn() {
        System.out.println("我是学生,我想学习");
    }
}

MyProxy.java(代理类)


import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class StudentProxy implements MethodInterceptor {
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("代理前 -------");
        proxy.invokeSuper(obj, args);
        System.out.println("代理后 -------");
        return null;
    }

}

测试类(Test.java


import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;

public class Test {
    public static void main(String[] args) {
        // 代理类class文件存入本地磁盘方便我们反编译查看源码
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/aphysia/Desktop");

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Student.class);
        enhancer.setCallback(new StudentProxy());
        Student student = (Student) enhancer.create();
        student.learn();

    }
}

运行之后的结果是:

CGLIB debugging enabled, writing to '/Users/xuwenhao/Desktop'
代理前 -------
我是学生,我想学习
代理后 -------

在我们选择的文件夹里面,生成了代理类的代码:

源码分析

我们先要代理的类,需要实现MethodInterceptor(方法拦截器)接口,这个接口只有一个方法 intercept,参数分别是:

  • obj:需要增强的对象
  • method:需要拦截的方法
  • args:要被拦截的方法参数
  • proxy:表示要触发父类的方法对象
package net.sf.cglib.proxy;
import java.lang.reflect.Method;
public interface MethodInterceptor extends Callback {
    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                               MethodProxy proxy) throws Throwable;
}

再看回我们要创建代理类的方法 enhancer.create(),这个方法的意思:如果需要,生成一个新类,并使用指定的回调(如果有的话)来创建一个新的对象实例。使用超类的无参数构造函数。

    public Object create() {
        classOnly = false;
        argumentTypes = null;
        return createHelper();
    }

主要的方法逻辑我们得看 createHelper(),除了校验,就是调用 KEY_FACTORY.newInstance() 方法生成 EnhancerKey对象,KEY_FACTORY 是静态 EnhancerKey 接口,newInstance()是接口里面的一个方法,重点在super.create(key)里面,调用的是父类的方法:

    private Object createHelper() {
        // 校验
        preValidate();
        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;
        Object result = super.create(key);
        return result;
    }

AbstractClassGeneratorEnhancer 的父类,create(key) 方法的主要逻辑是获取类加载器,缓存获取类加载数据,然后再反射构造对象,里面有两个创造实例对象的方法:

  • fistInstance(): 不应该在常规流中调用此方法。从技术上讲,{@link #wrapCachedClass(Class)}使用{@link EnhancerFactoryData}作为缓存值,后者支持比普通的旧反射查找和调用更快的实例化。出于向后兼容性的原因,这个方法保持不变:只是以防它曾经被使用过。(我的理解是目前的逻辑不会走到这个分支,因为它比较忙,但是为了兼容,这个case还保存着),内部逻辑其实用的是ReflectUtils.newInstance(type)
  • nextInstance(): 真正的创建代理对象的类
    protected Object create(Object key) {
        try {
            ClassLoader loader = getClassLoader();
            Map<ClassLoader, ClassLoaderData> cache = CACHE;
            ClassLoaderData data = cache.get(loader);
            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;
            Object obj = data.get(this, getUseCache());
            if (obj instanceof Class) {
                return firstInstance((Class) obj);
            }
            // 真正创建对象的方法
            return nextInstance(obj);
        } catch (RuntimeException e) {
            throw e;
        } catch (Error e) {
            throw e;
        } catch (Exception e) {
            throw new CodeGenerationException(e);
        }
    }

这个方法定义在AbstractClassGenerator,但是实际上是调用子类 Enhancer的实现,主要是通过获取参数类型,参数,以及回调对象,用这些参数反射生成代理对象。

    protected Object nextInstance(Object instance) {
        EnhancerFactoryData data = (EnhancerFactoryData) instance;

        if (classOnly) {
            return data.generatedClass;
        }

        Class[] argumentTypes = this.argumentTypes;
        Object[] arguments = this.arguments;
        if (argumentTypes == null) {
            argumentTypes = Constants.EMPTY_CLASS_ARRAY;
            arguments = null;
        }
        // 构造
        return data.newInstance(argumentTypes, arguments, callbacks);
    }

内部实现逻辑,调用的都是ReflectUtils.newInstance(), 参数种类不一样:

        public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {
            setThreadCallbacks(callbacks);
            try {

                if (primaryConstructorArgTypes == argumentTypes ||
                        Arrays.equals(primaryConstructorArgTypes, argumentTypes)) {
                    return ReflectUtils.newInstance(primaryConstructor, arguments);
                }
               return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments);
            } finally {
            
                setThreadCallbacks(null);
            }

        }

跟进去到底,就是获取构造器方法,反射方式构造代理对象,最终调用到的是 JDK 提供的方法:

    public static Object newInstance(Class type, Class[] parameterTypes, Object[] args) {
        return newInstance(getConstructor(type, parameterTypes), args);
    }
    public static Object newInstance(final Constructor cstruct, final Object[] args) {
            
        boolean flag = cstruct.isAccessible();
        try {
            if (!flag) {
                cstruct.setAccessible(true);
            }
            Object result = cstruct.newInstance(args);
            return result;
        } catch (InstantiationException e) {
            throw new CodeGenerationException(e);
        } catch (IllegalAccessException e) {
            throw new CodeGenerationException(e);
        } catch (InvocationTargetException e) {
            throw new CodeGenerationException(e.getTargetException());
        } finally {
            if (!flag) {
                cstruct.setAccessible(flag);
            }
        }
                
    }

打开它自动生成的代理类文件看看,就会发现其实也是生成那些方法,加上了一些增强方法:

生成的代理类继承了原来的类:

public class Student$$EnhancerByCGLIB$$929cb5fe extends Student implements Factory {
    ...
}

看看生成的增强方法,其实是调用到 intercept()方法,这个方法由我们前面自己实现,因此就完成了代理对象增强的功能:

    public final void learn() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$learn$0$Method, CGLIB$emptyArgs, CGLIB$learn$0$Proxy);
        } else {
            super.learn();
        }
    }

cglib 和 jdk 动态代理有什么区别

  1. jdk 动态代理是利用拦截器加上反射生成了一个代理接口的匿名类,执行方法的时候交给 InvokeHandler 处理。CGLIB 动态代理是使用了 ASM框架,修改原来的字节码,然后生成新的子类来处理。
  2. JDK 代理需要实现接口,但是CGLIB不强制。
  3. 在JDK1.6之前,cglib因为用了字节码生成技术,比反射效率高,但是之后jdk也进行了一些优化,效率上已经提升了。

【作者简介】
秦怀,公众号【秦怀杂货店】作者,技术之路不在一时,山高水长,纵使缓慢,驰而不息。个人写作方向:Java源码解析JDBCMybatisSpringredis分布式剑指OfferLeetCode等,认真写好每一篇文章,不喜欢标题党,不喜欢花里胡哨,大多写系列文章,不能保证我写的都完全正确,但是我保证所写的均经过实践或者查找资料。遗漏或者错误之处,还望指正。

剑指Offer全部题解PDF

2020年我写了什么?

开源编程笔记