公司建网站多少,上海公司注册在哪个区好,梁平集团网站建设,求做网站的有代理对象的循环依赖时#xff0c;如何确保代理对象能够正确持有原始对象的所有属性赋值结果#xff1f; 有代理对象的循环依赖时#xff0c;如何确保代理对象能够正确持有原始对象的所有属性赋值结果#xff1f;一、问题背景二、解答核心三、Spring 代理对象与属性赋值的… 有代理对象的循环依赖时如何确保代理对象能够正确持有原始对象的所有属性赋值结果 有代理对象的循环依赖时如何确保代理对象能够正确持有原始对象的所有属性赋值结果一、问题背景二、解答核心三、Spring 代理对象与属性赋值的核心机制1. 代理对象的本质2. 属性赋值的对象3. 三级缓存的作用4. 关键点代理对象如何“继承”属性 四、详细流程分析1. 阶段一创建 Bean A2. 阶段二创建 Bean B3. 阶段三完成 Bean A 的创建4. 阶段四代理对象访问属性 五、为什么代理对象能正确获取属性值1. CGLIB 代理的实现2. JDK 动态代理的实现3. Spring 的缓存管理 源码见mini-spring 有代理对象的循环依赖时如何确保代理对象能够正确持有原始对象的所有属性赋值结果
一、问题背景
在 Spring 解决有代理对象的循环依赖时核心是通过三级缓存singletonFactories和 SmartInstantiationAwareBeanPostProcessor 的 getEarlyBeanReference 方法提前生成代理对象如 AProxy。但是代理对象生成时原始对象如 A可能尚未完成属性注入例如 A 的属性 b 尚未赋值。你的疑问是
提前生成的代理对象 AProxy 是基于“半成品”原始对象 A 创建的此时 A 的属性如 b尚未赋值。在后续 A 的创建过程中属性注入是针对原始对象 A 完成的。最终容器中的 AProxy 如何确保其属性如 b是正确赋值的 二、解答核心
Spring 的代理对象如通过 CGLIB 或 JDK 动态代理生成的 AProxy通常是对原始对象 A 的包装而不是一个独立的对象。代理对象通过持有原始对象的引用称为 target在调用时将请求委托给原始对象。因此对原始对象 A 的属性赋值会直接反映到代理对象 AProxy 中因为代理对象内部的逻辑依赖于原始对象的状态。
以下从机制和流程两个方面详细解答。 三、Spring 代理对象与属性赋值的核心机制
1. 代理对象的本质
JDK 动态代理基于接口代理对象实现与目标对象相同的接口内部通过 InvocationHandler 持有原始对象的引用。CGLIB 代理基于子类代理对象是原始对象的子类继承了原始对象的字段并通过 super 访问或直接持有原始对象的引用。在 Spring 的循环依赖场景中代理对象AProxy通常通过 CGLIB 生成代理对象会直接包含原始对象 A 的字段或者通过某种方式如委托模式访问原始对象的字段。
2. 属性赋值的对象
在 Spring 的 Bean 创建过程中属性注入如 Autowired 或 Setter 注入是直接针对原始对象 A 完成的。代理对象 AProxy 本身并不直接参与属性注入而是通过引用原始对象 A 来访问其属性值。
3. 三级缓存的作用
三级缓存singletonFactories存储 ObjectFactory通过 getEarlyBeanReference 提前生成代理对象 AProxy。二级缓存earlySingletonObjects保存提前暴露的代理对象 AProxy供依赖方如 B使用。一级缓存singletonObjects最终存储完全初始化的代理对象 AProxy。
4. 关键点代理对象如何“继承”属性
CGLIB 代理AProxy 是 A 的子类继承了 A 的所有字段。属性注入直接修改 A 的字段而 AProxy 作为子类实例天然持有这些字段值。JDK 动态代理AProxy 通过 InvocationHandler 持有原始对象 A 的引用任何对字段的访问如 getB()都会委托给原始对象 A从而获取注入的属性值。因此无论哪种代理方式属性注入的结果都会反映在代理对象的行为中因为代理对象最终依赖原始对象的状态。 四、详细流程分析
让我们以文章中的例子A 依赖 BB 依赖 A且 A 被 AOP 代理来梳理整个流程重点说明属性赋值如何传递到代理对象。
1. 阶段一创建 Bean A
步骤 Spring 调用 A 的构造方法生成原始对象 A尚未进行属性注入。将 A 封装为 ObjectFactory加入三级缓存singletonFactories其中 ObjectFactory.getObject() 调用 getEarlyBeanReference。A 需要注入属性 b类型为 B触发 B 的创建。 状态 此时原始对象 A 的字段 b 为 null。三级缓存中存储了 A 的 ObjectFactory。
2. 阶段二创建 Bean B
步骤 Spring 创建 B 的原始对象加入三级缓存。B 需要注入 A字段 aSpring 调用 getSingleton(“A”) 一级缓存singletonObjects和二级缓存earlySingletonObjects中没有 A。从三级缓存singletonFactories获取 A 的 ObjectFactory调用 getObject()。getEarlyBeanReference 被触发SmartInstantiationAwareBeanPostProcessor如 AbstractAdvisorAutoProxyCreator生成 A 的代理对象 AProxy。AProxy 加入二级缓存earlySingletonObjects从三级缓存移除。 B 的字段 a 被注入 AProxyB 完成创建放入一级缓存。 状态 B 的字段 a 持有 AProxy。原始对象 A 的字段 b 仍为 null因为 A 的属性注入尚未完成。二级缓存中存储了 AProxy。
3. 阶段三完成 Bean A 的创建
步骤 返回到 A 的创建流程Spring 对原始对象 A 进行属性注入 调用 Setter 方法如 setB(B b)或直接通过反射设置字段 b。从一级缓存获取已创建的 B注入到原始对象 A 的字段 b。 原始对象 A 的字段 b 现在持有 B 的引用。Spring 继续执行 A 的初始化调用 BeanPostProcessor 的 postProcessAfterInitialization 检查 earlyProxyReferences发现 A 已提前生成代理AProxy因此不重复生成。 Spring 从二级缓存获取 AProxy将其放入一级缓存singletonObjects。 状态 原始对象 A 的字段 b 已注入 B。AProxy 作为 A 的子类CGLIB或通过 InvocationHandler 引用 AJDK 代理可以访问 A 的字段 b。一级缓存中的 AProxy 是最终暴露的对象B 中的字段 a 也指向 AProxy。
4. 阶段四代理对象访问属性
访问机制 当通过 AProxy 调用方法如 getB()时 CGLIB 代理AProxy 继承了 A 的字段getB() 直接返回 A 的字段 b已注入 B。JDK 动态代理AProxy 的 InvocationHandler 将 getB() 调用委托给原始对象 A返回字段 b 的值。 因此AProxy.getB() 返回注入的 B属性赋值结果成功反映在代理对象上。 结果 容器中的 A 是 AProxyB 中的 a 也是 AProxy引用一致。AProxy 能够正确访问原始对象 A 的属性值如 b代理逻辑如 AOP 切面也能正常触发。 五、为什么代理对象能正确获取属性值
1. CGLIB 代理的实现
机制 CGLIB 生成的 AProxy 是 A 的子类继承了 A 的所有字段如 private B b。属性注入直接修改原始对象 A 的字段 b而 AProxy 作为子类实例共享这些字段值。调用 AProxy.getB() 时直接访问继承的字段 b返回注入的 B。 代码示例简化class A {private B b;public B getB() { return b; }public void setB(B b) { this.b b; }
}
class AProxy extends A {// 继承了 A 的字段 bOverridepublic B getB() {// AOP 拦截逻辑return super.getB(); // 访问父类 A 的字段 b}
}结论 属性注入修改了原始对象 A 的字段而 AProxy 继承了这些字段因此能直接访问注入的属性值。
2. JDK 动态代理的实现
机制 JDK 动态代理生成 AProxy实现与 A 相同的接口如 IA并通过 InvocationHandler 持有原始对象 A 的引用。属性注入修改原始对象 A 的字段 b。调用 AProxy.getB() 时InvocationHandler 将调用转发到原始对象 A返回字段 b 的值。 代码示例简化interface IA {B getB();
}
class A implements IA {private B b;public B getB() { return b; }public void setB(B b) { this.b b; }
}
class AProxyHandler implements InvocationHandler {private A target;AProxyHandler(A target) { this.target target; }public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// AOP 拦截逻辑return method.invoke(target, args); // 转发到原始对象 A}
}结论 属性注入修改了原始对象 A 的字段AProxy 通过 InvocationHandler 访问 A 的字段因此能获取注入的属性值。
3. Spring 的缓存管理
Spring 确保代理对象 AProxy 是最终暴露的对象一级缓存和 B 的字段 a 都引用 AProxy。属性注入发生在原始对象 A 上但代理对象通过继承CGLIB或委托JDK 代理访问这些属性值确保行为一致。