SpringAOP[N]-Cglib代理问题
2021/11/17 23:13:51
本文主要是介绍SpringAOP[N]-Cglib代理问题,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
1. 内部调用不被代理
Cglib生成的代理类继承被代理类,代理类实例持有 target 实例。
相当于 proxy 有个字段 target,然后我们持有 proxy,当调用 proxy.method,代理实例 proxy 当然知道我们调用了哪个方法,于是进行 taget.method 调用,当在 target.method 内调用 this.methodInternal 时,this 是谁?显然是 target ,要注意 proxy 和 target 是两个实例,在 proxy.method 内调用 this.methodInternal 和在 target.method 内调用 this.methodInternal 是不一样的,前者调用被 proxy 重写的(代理)methodInterenal,后者的运行时类型就是 target 类型而非 proxy 代理后类型,因此两者之间不存在多态
2. protected、public字段
当我们在 target 中声明 protected、public 的字段,那么在 proxy 中是可以访问的,但是得到的都是 null
之前我就有这种情况,使用 @Async 异步调用,为了方便测试,内部字段使用 public 访问,结果总是 NPE(即便你的字段使用 new Xxx 赋予默认值了)
这里的问题就是 Spring 创建 Cglib 动态代理实例的时候是如何创建的,如果是普通构造函数调用显然父类的字段会被初始化的(构造函数 OR 默认值 OR 初始化块)
org.springframework.aop.framework.ObjenesisCglibAopProxy#createProxyClassAndInstance 这里创建实例,向下还有几层使用策略模式调用的,略过,直接说最终的
SunReflectionFactoryInstantiator 进行实例化
public class SunReflectionFactoryInstantiator<T> implements ObjectInstantiator<T> { private final Constructor<T> mungedConstructor; // 要实例化的 Class 类型, 动态代理实例化时是生成的代理类 Class public SunReflectionFactoryInstantiator(Class<T> type) { // 这里获取 Object 类的构造函数, 不是代理类的 Constructor<Object> javaLangObjectConstructor = getJavaLangObjectConstructor(); // 这个才是最终用来实例化的构造函数 this.mungedConstructor = SunReflectionFactoryHelper.newConstructorForSerialization(type, javaLangObjectConstructor); // .. this.mungedConstructor.setAccessible(true); } // 实例化 public T newInstance() { try { // 注意这里使用的构造函数 return this.mungedConstructor.newInstance((Object[])null); } catch (Exception var2) { throw new ObjenesisException(var2); } } private static Constructor<Object> getJavaLangObjectConstructor() { try { return Object.class.getConstructor((Class[])null); } catch (NoSuchMethodException var1) { throw new ObjenesisException(var1); } } }
SunReflectionFactoryHelper 可以通过 Object 构造函数获取指定类型的构造函数
class SunReflectionFactoryHelper { SunReflectionFactoryHelper() { } // 对于 SunReflectionFactoryInstantiator 来说, type 是代理类 Class, constructor 是 Object 的构造函数 public static <T> Constructor<T> newConstructorForSerialization(Class<T> type, Constructor<?> constructor) { // 这个是JDK的ReflectionFactory, 这个是为后面JDK可能不提供给用户使用吗? Class<?> reflectionFactoryClass = getReflectionFactoryClass(); // ReflectionFactory.getReflectionFactory 调用, 得到的是 ReflectionFactory 实例, 类似 Unsafe 一样的单例模式 Object reflectionFactory = createReflectionFactory(reflectionFactoryClass); // 获取 ReflectionFactory.newConstructorForSerialization Method Method newConstructorForSerializationMethod = getNewConstructorForSerializationMethod(reflectionFactoryClass); try { // ReflectionFactory.newConstructorForSerialization 调用 return (Constructor)newConstructorForSerializationMethod.invoke(reflectionFactory, type, constructor); } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException var6) { throw new ObjenesisException(var6); } } private static Class<?> getReflectionFactoryClass() { try { return Class.forName("sun.reflect.ReflectionFactory"); } catch (ClassNotFoundException var1) { throw new ObjenesisException(var1); } } private static Object createReflectionFactory(Class<?> reflectionFactoryClass) { try { Method method = reflectionFactoryClass.getDeclaredMethod("getReflectionFactory"); return method.invoke((Object)null); } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException | NoSuchMethodException var2) { throw new ObjenesisException(var2); } } private static Method getNewConstructorForSerializationMethod(Class<?> reflectionFactoryClass) { try { return reflectionFactoryClass.getDeclaredMethod("newConstructorForSerialization", Class.class, Constructor.class); } catch (NoSuchMethodException var2) { throw new ObjenesisException(var2); } } }
SunReflectionFactoryHelper 是 default 访问级别的,可以复制代码到自己的类中处理
测试,会发现 Demo 实例的字段并未被初始化
Constructor<Demo> demoConstructor = SunReflectionFactoryHelper.newConstructorForSerialization(Demo.class, Object.class.getDeclaredConstructor(null)); Demo demo = demoConstructor.newInstance(null);
Spring 的 Cglib 代理的目的就是为了代理方法,达到类似 JDK 动态代理一样的效果,因此对于继承的父类的东西是不管的,只是能通过父类方法访问代理类,从而实现代理
这篇关于SpringAOP[N]-Cglib代理问题的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-05-09“2024鸿蒙零基础快速实战-仿抖音App开发(ArkTS版)”实战课程已上线
- 2024-05-09聊聊如何通过arthas-tunnel-server来远程管理所有需要arthas监控的应用
- 2024-05-09log4j2这么配就对了
- 2024-05-09nginx修改Content-Type
- 2024-05-09Redis多数据源,看这篇就够了
- 2024-05-09Google Chrome驱动程序 124.0.6367.62(正式版本)去哪下载?
- 2024-05-09有没有大佬知道这种数据应该怎么抓取呀?
- 2024-05-09这种运行结果里的10.100000001,怎么能最快改成10.1?
- 2024-05-09企业src漏洞挖掘-有意思的命令执行
- 2024-05-08阿里云域名注册流程,分享给第一次购买域名的新手站长!