Spring---循环依赖
总结
什么是循环依赖?
A对象中有属性B对象,B对象中有属性A对象;
Spring如何解决循环依赖?
1、将Bean的创建分成 实例化 + 初始化 2个过程;
2、提供了3级缓存维护单例Bean:
- singletonObjects:保存已经初始化完毕的单例Bean。
- earlySingletonObjects:保存已经被实例化但未完全初始化的Bean。 这些Bean是为了解决循环依赖而提前暴露出来的。
- singletonFactories:保存用来生成Bean的ObjectFactory。 当需要提前暴露Bean时,会从这里获取对应的ObjectFactory并调用getObject()方法得到Bean的早期引用。
-
创建Bean实例:
- Spring首先尝试创建一个Bean A的实例。
- 如果发现Bean A依赖于Bean B,那么Spring会暂停A的初始化,并开始创建Bean B。
-
提前暴露早期引用:
- 在创建Bean B的过程中,如果B又依赖于A,Spring会在
singletonFactories
中为A创建一个ObjectFactory,并将这个工厂放入singletonFactories
缓存。 - 然后Spring从
singletonFactories
获取A的ObjectFactory并调用其getObject()
方法得到A的一个早期引用(未完全初始化的对象),并将这个早期引用放入earlySingletonObjects
缓存。 - 使用A的早期引用继续B的初始化过程。
- 在创建Bean B的过程中,如果B又依赖于A,Spring会在
-
完成依赖注入:
- 当B完成初始化后,它会被放入
singletonObjects
缓存。 - 接着,Spring返回到A的初始化过程,完成A的属性注入和其他初始化工作。
- 当B完成初始化后,它会被放入
-
完成Bean A的初始化:
- 一旦A的所有依赖都已注入,并且所有初始化回调(如
@PostConstruct
注解的方法或InitializingBean.afterPropertiesSet()
等)都执行完毕,A就被认为是完全初始化的。 - 完全初始化后的A从
earlySingletonObjects
移除,并被放入singletonObjects
缓存,成为完整的单例Bean。
- 一旦A的所有依赖都已注入,并且所有初始化回调(如
当Spring尝试创建一个Bean A时,如果发现A依赖于Bean B,而B又依赖于A,那么Spring会执行以下步骤:
- 创建Bean A的实例,此时A处于创建中状态。
- 尝试创建Bean B的实例,同样B也处于创建中状态。
- 在创建B的过程中,发现它依赖于A,但由于A已经在创建过程中,Spring会从
singletonFactories
中获取A的ObjectFactory,然后调用getObject()方法得到A的早期引用。 - 使用A的早期引用完成B的初始化。
- 完成A的初始化,将其放入
singletonObjects
中。
3、在Spring中,提供的DI方式中:
- 构造器注入:如果使用构造器注入(constructor injection),Spring将无法解决循环依赖问题,因为每个Bean都需要另一个完全初始化的Bean作为其构造参数。在这种情况下,Spring会抛出
BeanCurrentlyInCreationException
。 - Setter注入(属性注入&setter注入):Spring可以使用setter注入(field injection)来解决循环依赖问题。当一个Bean正在被创建但尚未完成时,Spring会提前暴露这个Bean的一个早期引用(early reference)。这样,其他依赖它的Bean就可以获得这个早期引用,并继续它们的初始化过程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | /** * what? * A对象中有属性B对象,B对象中有属性A对象; * 创建任何对象,都是先实例化,后初始化; * * 如何解决循环依赖? * 1、实例化(createBeanInstance) 与 初始化(populateBean,initializeBean) 分开; * 2、设置缓存预存对象(先将 先实例化的A对象 缓存起来,在B对象初始化设置属性A时,从 缓存中获取A实例,进行赋值); * * 使用半成品对象会有问题,如何解决? * spring3级缓存:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry * * 1级缓存: * private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); * k:beanName * v:bean * * 2级缓存: * private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); * k:beanName * v:本成品bean * * 3级缓存: * private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); * k:beanName * v:lambada表达式 * */ |
分类:
Spring
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
2020-03-23 JavaSE---进制