企业网站建设套餐上海,计算机基础培训机构,怎样做商城网站,专业的手机网站建设公司排名Spring是如何实现有代理对象的循环依赖 代理对象介入后所产生问题阶段一#xff1a;A 开始创建阶段二#xff1a;A 注入 B#xff0c;转而开始创建 B阶段三#xff1a;B 从缓存中获取 A 的引用阶段四#xff1a;A 初始化完成#xff0c;进行代理增强 解决代理对象的循环依… Spring是如何实现有代理对象的循环依赖 代理对象介入后所产生问题阶段一A 开始创建阶段二A 注入 B转而开始创建 B阶段三B 从缓存中获取 A 的引用阶段四A 初始化完成进行代理增强 解决代理对象的循环依赖问题ObjectFactory 接口三级缓存的引入DefaultSingletonBeanRegistry提前暴露代理对象AbstractAutowireCapableBeanFactory从三级缓存中获取代理对象getSingleton()代理对象的生成时机有代理对象的循环依赖全流程阶段一Spring 开始创建 Bean A阶段二A 依赖注入 B触发对 B 的创建流程阶段三B 需要注入 A尝试从缓存中获取 问题所在 源码见mini-spring 要解决有代理对象的循环依赖问题首先要明白代理对象介入时候产生循环依赖的原因这里是以解决了无代理对象的循环依赖为背景进行解释的。
代理对象介入后所产生问题
那我们可以分析一下为什么有了代理对象之后会产生循环依赖看一下下面的例子
public class A { Autowired private B b; public void func() {} public B getB() { return b; } public void setB(B b) { this.b b; }
}public class B { Autowired private A a; public A getA() { return a; } public void setA(A a) { this.a a; }
}阶段一A 开始创建
Spring 创建了原始的 A 实例还没有完成属性注入这个“半成品 A”被放入 二级缓存准备暴露 early reference但注意此时这个对象还不是代理只是最原始的 A。
阶段二A 注入 B转而开始创建 B
Spring 去创建 B为了能让 B 注入依赖先把 B 的原始实例放入三级缓存B 也还没初始化完但它要注入 A于是 Spring 尝试从缓存中获取 A。
阶段三B 从缓存中获取 A 的引用
这一步就是关键此时 A 还未完成代理增强Spring 会从三级缓存中调用 getEarlyBeanReference() 获取 A 的 early reference理论上可以是代理但如果没有设置好返回的是原始对象于是B 中注入了原始 A 的引用而不是代理对象。
阶段四A 初始化完成进行代理增强
初始化流程继续走完Spring 对 A 执行了 BeanPostProcessor此时 A 被包装成了代理对象比如 AProxySpring 将 AProxy 注册进一级缓存singletonObjects作为最终使用的 Bean。
此时容器中拿到的是代理对象 AProxy但注入进 B 中的还是早期原始对象 A
AProxy ≠ A逻辑上是同一个业务对象但代理层包裹的逻辑如事务、切面无法被触发结果就是外部调用 AProxy 时能进入切面逻辑而 B 中注入的 A 却绕过了代理逻辑这就导致了预期之外的行为甚至引发潜在错误。
解决代理对象的循环依赖问题
在前文的描述中我们已经明确了为什么当代理对象介入时会使循环依赖问题变得复杂。简单来说问题的根源在于提前暴露的是原始 Bean 的引用而不是代理对象的引用。因此解决该问题的关键在于在存在代理对象的情况下如何提前暴露代理对象的引用。 ObjectFactory 接口
public interface ObjectFactoryT {/*** 创建并返回一个泛型类型 T 的对象。* 该方法抽象化了对象的获取过程允许延迟创建* 调用者无需关心对象的具体创建细节。** return 泛型类型 T 的对象实例*/T getObject();
}在 Spring 中ObjectFactoryT 是一个功能性接口常用于延迟获取对象实例。它的典型使用场景包括懒加载、循环依赖处理、作用域管理等。通过封装一个 getObject() 方法Spring 可以在真正需要某个对象时再执行创建逻辑避免过早初始化。 三级缓存的引入DefaultSingletonBeanRegistry
// 三级缓存ObjectFactory 封装的早期引用
protected final MapString, ObjectFactory? singletonFactories new ConcurrentHashMap(16);public void addSingletonFactory(String beanName, ObjectFactory? singletonFactory) {singletonFactories.put(beanName, singletonFactory);
}Spring 通过在 DefaultSingletonBeanRegistry 中引入三级缓存 singletonFactories来保存 Bean 的“早期引用”Early Reference。而这些引用由 ObjectFactory 封装以支持延迟获取。 提前暴露代理对象AbstractAutowireCapableBeanFactory
if (beanDefinition.isSingleton()) {Object finalBean bean;addSingletonFactory(beanName, () - getEarlyBeanReference(beanName, finalBean));
}在 Bean 实例化完成之后但尚未进行属性填充之前Spring 会将其通过 ObjectFactory 封装后加入三级缓存。关键在于此时调用 getEarlyBeanReference()如果需要创建代理对象就在这个阶段完成。
private Object getEarlyBeanReference(String beanName, Object bean) {Object exposedObject bean;for (BeanPostProcessor processor : getBeanPostProcessors()) {if (processor instanceof SmartInstantiationAwareBeanPostProcessor) {exposedObject ((SmartInstantiationAwareBeanPostProcessor) processor).getEarlyBeanReference(exposedObject, beanName);}}return exposedObject;
}这段逻辑的核心作用是如果某个 BeanPostProcessor 支持提前生成代理如 AOP就允许它在此阶段返回代理对象。 从三级缓存中获取代理对象getSingleton()
#DefaultSingletonBeanRegistry
Override
public Object getSingleton(String beanName) {Object singletonObject singletonObjects.get(beanName);if (singletonObject null) {singletonObject earlySingletonObjects.get(beanName);if (singletonObject null) {ObjectFactory? singletonFactory singletonFactories.get(beanName);if (singletonFactory ! null) {singletonObject singletonFactory.getObject();earlySingletonObjects.put(beanName, singletonObject);singletonFactories.remove(beanName);}}}return singletonObject;
}在创建某个 Bean 的过程中如果发生了依赖注入需求如 A 注入 BB 又注入 A则可以通过 getSingleton() 从三级缓存中获取提前暴露的代理对象从而避免注入的是原始未增强的对象实例。 代理对象的生成时机
#SmartInstantiationAwareBeanPostProcessor
public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {Object getEarlyBeanReference(Object bean, String beanName);
}Spring 中的代理生成主要依赖 SmartInstantiationAwareBeanPostProcessor。其方法 getEarlyBeanReference 允许在 Bean 完整初始化前就返回代理对象。
#AbstractAdvisorAutoProxyCreator
Override
public Object getEarlyBeanReference(Object bean, String beanName) {earlyProxyReferences.add(beanName);return wrapIfNecessary(bean, beanName); // 判断是否需要创建代理
}Override
public Object postProcessAfterInitialization(Object bean, String beanName) {if (earlyProxyReferences.contains(beanName)) {return bean;}return wrapIfNecessary(bean, beanName);
}当一个 Bean 被代理时如果我们只是将原始对象暴露给其他依赖它的 Bean就可能导致依赖方拿到的是未增强的实例进而绕过了 AOP 等代理逻辑。Spring 通过三级缓存 ObjectFactory SmartInstantiationAwareBeanPostProcessor实现了在 Bean 实例化之后但属性注入之前提前暴露真正的代理对象引用从而优雅地解决了带有代理对象的循环依赖问题。
其实到这里还有一个问题我们先来思考一下有代理对象的循环依赖全流程
有代理对象的循环依赖全流程
阶段一Spring 开始创建 Bean A Spring 首先尝试创建 Bean A执行构造方法生成一个尚未完成依赖注入的“半成品 A” 按照三级缓存机制的设计 A 的原始实例会被封装成一个 ObjectFactory 放入三级缓存singletonFactories 这一步的关键是尚未完成依赖注入的 A已经为可能的循环依赖提前“准备好了引用”。 ⚠️ 注意这时候的 A 并不是代理对象只是最原始的实例。代理对象的创建通常发生在初始化之后。 阶段二A 依赖注入 B触发对 B 的创建流程 接下来Spring 发现 A 依赖 B于是进入创建 Bean B 的流程 同样地Spring 会为 B 创建一个原始实例并把 B 的 ObjectFactory 提前放入三级缓存 但此时B 也依赖 A于是 Spring 又尝试获取 A 的实例来注入到 B 中。 阶段三B 需要注入 A尝试从缓存中获取 这是整个流程的核心关键点 Spring 调用 getSingleton(A) 方法在一级、二级缓存都找不到 A因为尚未放入于是尝试从三级缓存中获取 调用 ObjectFactory.getObject()这实际上就是调用 getEarlyBeanReference(beanName, bean) 方法。
如果启用了 AOP如 Aspect这个方法就会在这里提前返回 A 的代理对象AProxy 于是B 拿到的是代理对象 AProxy注入完成。 这就是 Spring 解决“代理对象循环依赖”问题的关键——通过 getEarlyBeanReference 方法在依赖注入前就构造代理对象并暴露出来。 ✅ 最终B 中保存的就是完整的 AProxy 引用而不是原始 A这就避免了代理失效的问题。 问题所在
在B当中已经创建了A的代理对象并将其加入到了二级缓存当中那么回到 A 的创建流程中Spring 继续执行 A 的依赖注入、初始化等操作
由于之前已经通过 getEarlyBeanReference 创建了代理postProcessAfterInitialization 阶段会检测到该 Bean 已代理过不再重复代理所以在这里我们还需要修改部分逻辑
修改AbstractAutowireCapableBeanFactory的doCreateBean方法
// 创建完毕后加入缓存
if (beanDefinition.isSingleton()){ // 如果循环依赖创建了代理对象在这里不会去重复创建需要从缓存当中取出来 Object exposedObject getSingletonBean(beanName); super.addSingletonBean(beanName, exposedObject);
}最终Spring 将 AProxy 放入一级缓存作为 Bean A 的正式实例
此时 B 中的引用是 AProxyA 本身在容器中也是 AProxy引用一致 文章转载自: http://www.morning.sqtsl.cn.gov.cn.sqtsl.cn http://www.morning.nfsrs.cn.gov.cn.nfsrs.cn http://www.morning.yymlk.cn.gov.cn.yymlk.cn http://www.morning.wjndl.cn.gov.cn.wjndl.cn http://www.morning.srjbs.cn.gov.cn.srjbs.cn http://www.morning.bztzm.cn.gov.cn.bztzm.cn http://www.morning.cmldr.cn.gov.cn.cmldr.cn http://www.morning.synlt.cn.gov.cn.synlt.cn http://www.morning.dsprl.cn.gov.cn.dsprl.cn http://www.morning.yfnjk.cn.gov.cn.yfnjk.cn http://www.morning.bxqry.cn.gov.cn.bxqry.cn http://www.morning.mmxnb.cn.gov.cn.mmxnb.cn http://www.morning.jyjqh.cn.gov.cn.jyjqh.cn http://www.morning.bgxgq.cn.gov.cn.bgxgq.cn http://www.morning.prgyd.cn.gov.cn.prgyd.cn http://www.morning.qctsd.cn.gov.cn.qctsd.cn http://www.morning.tgnwt.cn.gov.cn.tgnwt.cn http://www.morning.sjpbh.cn.gov.cn.sjpbh.cn http://www.morning.gcysq.cn.gov.cn.gcysq.cn http://www.morning.yhglt.cn.gov.cn.yhglt.cn http://www.morning.bchhr.cn.gov.cn.bchhr.cn http://www.morning.cdrzw.cn.gov.cn.cdrzw.cn http://www.morning.kbkcl.cn.gov.cn.kbkcl.cn http://www.morning.czqqy.cn.gov.cn.czqqy.cn http://www.morning.gywxq.cn.gov.cn.gywxq.cn http://www.morning.mxmzl.cn.gov.cn.mxmzl.cn http://www.morning.ffbl.cn.gov.cn.ffbl.cn http://www.morning.zlsmx.cn.gov.cn.zlsmx.cn http://www.morning.rxcqt.cn.gov.cn.rxcqt.cn http://www.morning.dbjyb.cn.gov.cn.dbjyb.cn http://www.morning.xkwyk.cn.gov.cn.xkwyk.cn http://www.morning.bmfqg.cn.gov.cn.bmfqg.cn http://www.morning.tlpgp.cn.gov.cn.tlpgp.cn http://www.morning.pbpcj.cn.gov.cn.pbpcj.cn http://www.morning.bssjp.cn.gov.cn.bssjp.cn http://www.morning.kxypt.cn.gov.cn.kxypt.cn http://www.morning.xsgxp.cn.gov.cn.xsgxp.cn http://www.morning.wfbnp.cn.gov.cn.wfbnp.cn http://www.morning.amonr.com.gov.cn.amonr.com http://www.morning.bqpg.cn.gov.cn.bqpg.cn http://www.morning.qxmpp.cn.gov.cn.qxmpp.cn http://www.morning.fqklt.cn.gov.cn.fqklt.cn http://www.morning.pbdnj.cn.gov.cn.pbdnj.cn http://www.morning.lggng.cn.gov.cn.lggng.cn http://www.morning.xxknq.cn.gov.cn.xxknq.cn http://www.morning.kxnnh.cn.gov.cn.kxnnh.cn http://www.morning.wmsgt.cn.gov.cn.wmsgt.cn http://www.morning.pwgzh.cn.gov.cn.pwgzh.cn http://www.morning.qlrtd.cn.gov.cn.qlrtd.cn http://www.morning.dnqpq.cn.gov.cn.dnqpq.cn http://www.morning.wkhfg.cn.gov.cn.wkhfg.cn http://www.morning.rhqn.cn.gov.cn.rhqn.cn http://www.morning.qbkw.cn.gov.cn.qbkw.cn http://www.morning.wyjhq.cn.gov.cn.wyjhq.cn http://www.morning.ddzqx.cn.gov.cn.ddzqx.cn http://www.morning.zwzwn.cn.gov.cn.zwzwn.cn http://www.morning.kjnfs.cn.gov.cn.kjnfs.cn http://www.morning.ahlart.com.gov.cn.ahlart.com http://www.morning.zfqdt.cn.gov.cn.zfqdt.cn http://www.morning.pnmgr.cn.gov.cn.pnmgr.cn http://www.morning.ltpph.cn.gov.cn.ltpph.cn http://www.morning.rkypb.cn.gov.cn.rkypb.cn http://www.morning.kkwbw.cn.gov.cn.kkwbw.cn http://www.morning.hlxpz.cn.gov.cn.hlxpz.cn http://www.morning.mwcqz.cn.gov.cn.mwcqz.cn http://www.morning.bjsites.com.gov.cn.bjsites.com http://www.morning.qjghx.cn.gov.cn.qjghx.cn http://www.morning.wklhn.cn.gov.cn.wklhn.cn http://www.morning.zglrl.cn.gov.cn.zglrl.cn http://www.morning.ltrms.cn.gov.cn.ltrms.cn http://www.morning.tnjkg.cn.gov.cn.tnjkg.cn http://www.morning.rfjmy.cn.gov.cn.rfjmy.cn http://www.morning.rsjf.cn.gov.cn.rsjf.cn http://www.morning.qbrs.cn.gov.cn.qbrs.cn http://www.morning.wsnbg.cn.gov.cn.wsnbg.cn http://www.morning.qnlbb.cn.gov.cn.qnlbb.cn http://www.morning.lnsnyc.com.gov.cn.lnsnyc.com http://www.morning.ptxwg.cn.gov.cn.ptxwg.cn http://www.morning.jlthz.cn.gov.cn.jlthz.cn http://www.morning.tqgmd.cn.gov.cn.tqgmd.cn