共计 1129 个字符,预计需要花费 3 分钟才能阅读完成。
Spring 解决循环依赖是有前置条件的:
- 呈现循环依赖的 Bean 必须要是 单例
- 依赖注入的形式 不能全是结构器注入 的形式
依赖状况 | 依赖注入形式 | 循环依赖是否被解决 |
---|---|---|
AB 相互依赖(循环依赖) | 均采纳 setter 办法注入 | 是 |
AB 相互依赖(循环依赖) | 均采纳结构器注入 | 否 |
AB 相互依赖(循环依赖) | A 中注入 B 的形式为 setter 办法,B 中注入 A 的形式为结构器 | 是 |
AB 相互依赖(循环依赖) | B 中注入 A 的形式为 setter 办法,A 中注入 B 的形式为结构器 | 否 |
创立 A 的过程实际上就是调用 getBean
办法,这个办法有两层含意
- 创立一个新的 Bean
- 从 缓存 中获取到曾经被创立的对象
????
Spring 通过 三级缓存解决了循环依赖 ,其中一级缓存为 单例池 (singletonObjects
), 二级缓存为 晚期曝光对象 (earlySingletonObjects
),三级缓存为 晚期曝光对象工厂(singletonFactories
)。
getSingleton(beanName, true)
这个办法实际上就是到缓存中尝试去获取 Bean,整个缓存分为三级
singletonObjects
,一级缓存,存储的是所有 曾经齐全创立好了的单例 BeanearlySingletonObjects
,实现实例化,然而还未进行属性注入及初始化的对象singletonFactories
,提前裸露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象
当 A、B 两个类产生循环援用时,在 A 实现实例化后,就 应用实例化后的对象去创立一个对象工厂 , 并增加到三级缓存中,如果 A 被 AOP 代理,那么通过这个工厂获取到的就是 A 代理后的对象,如果 A 没有被 AOP 代理,那么这个工厂获取到的就是 A 实例化的对象。
当 A 进行属性注入时,会去创立 B,同时 B 又依赖了 A,所以创立 B 的同时又会去调用 getBean(a)
来获取须要的依赖,此时的 getBean(a)
会从缓存中获取:
第一步,先获取到三级缓存中的工厂;
第二步,调用对象工工厂的 getObject 办法来获取到对应的对象,失去这个对象后将其注入到 B 中。紧接着 B 会走完它的生命周期流程,包含初始化、后置处理器等。
当 B 创立完后,会将 B 再注入到 A 中,此时 A 再实现它的整个生命周期。至此,循环依赖完结!
面试官:”为什么要应用三级缓存呢?二级缓存能解决循环依赖吗?“
答:如果要应用二级缓存解决循环依赖,意味着 所有 Bean 在实例化后就要实现 AOP 代理 ,这样违反了 Spring 设计的准则,Spring 在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator
这个后置处理器来在 Bean 生命周期的最初一步来实现 AOP 代理,而不是在实例化后就立马进行 AOP 代理。
转载:讲一讲 Spring 中的循环依赖