Spring--如何解决循环依赖
利用三级缓存解决,三级缓存都是什么?
一级缓存:singletonObjects,存放可用的成品Bean
。
二级缓存:earlySingletonObjects,存放半成品的Bean
,半成品的Bean
是已创建对象,但是未注入属性和初始化。用以解决循环依赖。
三级缓存:存的是Bean工厂对象
,用来生成半成品的Bean
并放入到二级缓存中。用以解决循环依赖。(会将需要被增强的类,提前增强。添加AOP缓存,后续通过这个缓存判断是否被增强,保证不重复增强。)
如何解决循环依赖?
- 通过构建函数创建A对象(A对象是半成品,还没注入属性和调用init方法)。
- A对象需要注入B对象,发现缓存里还没有B对象,将
半成品对象A
放入半成品缓存
。 - 通过构建函数创建B对象(B对象是半成品,还没注入属性和调用init方法)。
- B对象需要注入A对象,从
半成品缓存
里取到半成品对象A
。 - B对象继续注入其他属性和初始化,之后将
完成品B对象
放入完成品缓存
。 - A对象继续注入属性,从
完成品缓存
中取到完成品B对象
并注入。 - A对象继续注入其他属性和初始化,之后将
完成品A对象
放入完成品缓存
。
为什么构造器注入属性的时候不能解决循环依赖问题?
Spring注入单例bean时,实例化和初始化是分开的,将提前实例化好的对象提前暴露出去,供别人使用。使用构造器的时候,必须要用构造方法,没有构造方法无法完成对象的实例化操作,无法创建对象,会陷入死循环中。
一级缓存能不能解决此问题?
不能。如果只有一级缓存,初始化完成和未初始化完成的对象都放在这个map中,拿到的可能是没有初始化的,会造成空指针异常。
二级缓存能不能解决此问题?
可以解决。但是,注入的对象实现了AOP(比如:代理对象),那么注入到其他bean的时候,不是最终的代理对象,而是原始的。通过三级缓存的ObjectFactory才能实现类最终的代理对象。
解决方案: 在提前曝光半成品
时,直接执行getEarlyBeanReference
创建到代理,并放入到缓存earlySingletonObjects
中。那就不需要通过ObjectFactory
来延迟
执行getEarlyBeanReference
,也就不需要singletonFactories
这一级缓存。
spring为什么不这么做?
如果要使用二级缓存
解决循环依赖
,意味着Bean在构造
完后就创建代理对象
,这样违背了Spring设计原则
。
Spring结合AOP跟Bean的生命周期,是在Bean创建完全
之后通过AnnotationAwareAspectJAutoProxyCreator
这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization
方法中对初始化后的Bean完成AOP代理。
如果出现了循环依赖
,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。