【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理

导读:本篇文章讲解 【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

1、基于JDK的动态代理

【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理

整个代理最核心的方法就是Object proxyInstance = Proxy.newProxyInstance(classLoader, interfaces, handler)。使用JDK动态代理的时候,必须要有InvocationHandler这个对象。

2、基于CGLib的动态代理

使用CGLib动态代理的时候,不需要指定接口

【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理

准备好自定义的测试类之后,开始Debug运行代码

【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理

进入Enhancer类中

// 使用key工厂创建出对应class的代理类,后面的keyFactory.
// HASH_ASM_TYPE即代理类中创建HashCode方法的策略
private static final EnhancerKey KEY_FACTORY =(EnhancerKey) KeyFactory.create(EnhancerKey.class, KeyFactory.HASH_ASM_TYPE, null);
这个KeyFactory就是为了拼接或生成某些Map或Set中的key值,只是这些key值比较特殊,里面包含n多个键而已。比如1、2、3是一个组合,2、3也是一个组合,都可以作为key

【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理

进入create方法中
// 创建约一个最简单的代理类生成器
Generator gen = new Generator();

// 设置接口为enhancerKey类型
gen.setInterface(keyInterface);

// 为了整合Spring而做准备
gen.setContextClass(keyInterface);

// 添加定制器,为了扩展做准备
gen.addCustomizer(customizer);

// 设置生成器的类加载器
gen.setClassLoader(loader);

// 生成enhancerKey的代理类
// 不是具体的MyCalculator类的代理类
gen.create();

【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理

继续进入到create方法中
// 设置了该生成器生成代理类的名字前缀
// 既接口名:Enhancer.enhancerKey
setNamePrefix(keyInterface.getName());

【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理

代码继续进入到create方法中
// 获取当前生成器的类加载器
ClassLoader loader = getClassLoader();

// 当前类加载器对应的缓存,缓存key为类加载器
// 缓存的value为ClassLoaderData
// ClassLoaderData中放了2个函数式接口(Function),后续会进行调用
Map<ClassLoader, ClassLoaderData> cache = CACHE;

// 从缓存中获取当前类加载器加载过的类
ClassLoaderData data = cache.get(loader);

…..

// 新建一个缓存Cache,将之前的缓存Cache的数据
// 添加进来,并将已经被GC回收的数据清除掉
Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);

// 新建一个当前加载器对应的ClassLoaderData,并加入到缓存中
// 但ClassLOaderData中此时还没有数据
data = new ClassLoaderData(loader);

// 刷新全局缓存
CACHE = newCache;

…..

// 设置全局key
this.key = key;

// 在刚刚创建的ClassLoaderData中调用get方法
Object obj = data.get(this, getUseCache());

【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理

代码继续进入到get方法中
// 如果不使用缓存(默认使用)
if (!useCache) {
    // 直接调用生成器的命令
    return gen.generate(ClassLoaderData.this);
} else {
    // 传入代理类生成器,并根据代理类生成器获取值返回
    Object cachedValue = generatedClasses.get(gen);
    // 解包装
    return gen.unwrapCachedValue(cachedValue);
}

【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理

代码继续进入到createEntry方法中

【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理

代码继续进入到generate方法中
// 当前的代理类生成器放入ThreadLocal中
CURRENT.set(this);

// 获取类加载器
ClassLoader classLoader = data.getClassLoader();

…..

synchronized (classLoader) {
    // 生成代理类名字
    String name = generateClassName(data.getUniqueNamePredicate());
    // 将这个名字放入缓存中
    data.reserveName(name);
    // 当前代理类生成器设置类名
    this.setClassName(name);
}

…..

// 生成字节码
byte[] b = strategy.generate(this);

【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理

代码继续进入到generateClass方法中
// 创建类写入聚合对象
ClassEmitter ce = new ClassEmitter(v);

// 找到被代理类的newInstance方法,如果没有则会报异常
// 也就是说如果想用Generator代理类生成器,必须要有newInstance方法
Method newInstance = ReflectUtils.findNewInstance(keyInterface);

// 如果被代理类的newInstance不为Object也会报异常
// 此处代理的Enhancer.EnhancerKey newInstance方法返回值为Object
if (!newInstance.getReturnType().equals(Object.class)) {
    throw new IllegalArgumentException(“newInstance method must return Object”);
}

// 找到newInstance方法的所有参数类型,并当做成员变量
Type[] parameterTypes = TypeUtils.getTypes(newInstance.getParameterTypes());

// 创建类开始写入类头、版本号、访问权限、类名等
ce.begin_class(Constants.V1_8,
               Constants.ACC_PUBLIC,
               getClassName(),
               KEY_FACTORY,
               new Type[]{Type.getType(keyInterface)},
               Constants.SOURCE_FILE);

// 写入无参构造方法
EmitUtils.null_constructor(ce);

// 写入newInstance方法
EmitUtils.factory_method(ce, ReflectUtils.getSignature(newInstance));

// 写入有参构造方法
CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC,
                    TypeUtils.parseConstructor(parameterTypes),
                    null);

// 有参构造方法中调用父类构造方法,super.xxx                    
e.super_invoke_constructor();

// 找到传入的方法定制器,比如:hashCode方法定制器等
List<FieldTypeCustomizer> fieldTypeCustomizers = getCustomizers(FieldTypeCustomizer.class);

// 遍历成员变量,也就是newInstance方法的所有参数
for (int i = 0; i < parameterTypes.length; i++) {

    …..

    // 将这些参数全部声明到写入类中
    ce.declare_field(Constants.ACC_PRIVATE | Constants.ACC_FINAL,
                    getFieldName(i),
                    fieldType,
                    null);

    …..

    // 设置每个成员变量的值
    e.putfield(getFieldName(i));
}

// 设置返回值
e.return_value();

// 有参构造和成员变量写入完成
e.end_method();

// 写入hashCode方法
e = ce.begin_method(Constants.ACC_PUBLIC, HASH_CODE, null);

…..

// 写入equals方法
e = ce.begin_method(Constants.ACC_PUBLIC, EQUALS, null);

…..

// 写入toString方法
e = ce.begin_method(Constants.ACC_PUBLIC, TO_STRING, null);

…..

// 类信息已经全部写入ClassVisitor中
ce.end_class();

【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理

代码继续往下走回到generate方法中
// 获取到字节码代表的class名字
String className = ClassNameReader.getClassName(new ClassReader(b));

…..

// 往内存中加载
gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain, contextClass);

代码继续往下走回到create方法中
// 返回生成好的代理类class信息
Object obj = data.get(this, getUseCache());

// 如果是class,则实例化class,并返回我们需要的代理类
if (obj instanceof Class) {
    return firstInstance((Class) obj);
}

// 如果不是则说明是实体,就直接执行另一个方法返回实体
return nextInstance(obj);

【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理

代码继续往下走回到main方法中,此时已经将enhancer对象创建完成。在后续创建过程中需要一个EnhancerKey的对象,所以在进行enhancer对象创建的时候需要先把EnhancerKey(newInstance)对象准备好,而这个对象也需要动态代理来创建。

【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理

代码继续往下走,进入create方法中的createHelper方法中
// 检验callbackTypes、filter是否为空,包括为空时的处理
preValidate();

// 通过newInstance方法来创建EnhancerKey对象
// 正常情况下只需要new一个对象即可调用方法
// 但是Key_Factory是一个EnhancerKey类型
// 是一个内部接口,需要动态代理来实现
// 最终是为了调用newInstance方法
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);

// 设置当前enhancer的代理类的key标识
this.currentKey = key;

// 调用父类,也就是AbstractClassGenerator的创建代理类
Object result = super.create(key);

【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理

代码继续往下走,进入super.create方法中,和刚刚创建enhancer代理一样的方法,而此时缓存中的值就不是空了

【闲聊杂谈】源码追踪Spring的JDK和CGLib动态代理

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/111913.html

(0)
Java光头强的头像Java光头强

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!