循环依赖
什么是循环依赖?
a对象中依赖b对象,b对象中依赖a对象,然后Spring在Bean对象属性填充时发现无法从IOC容器中发现已经实例化好的对象,因此无法完成自身的属性注入,因此bean生命周期终止,导致bean对象实例化失败。
Bean的生命周期
-
Bean 容器找到配置文件中 Spring Bean 的定义。
-
Bean 容器利用 Java Reflection API 创建一个Bean的实例。
-
如果涉及到一些属性值 利用
set()
方法设置一些属性值。 -
如果 Bean 实现了
BeanNameAware
接口,调用setBeanName()
方法,传入Bean的名字。 -
如果 Bean 实现了
BeanClassLoaderAware
接口,调用setBeanClassLoader()
方法,传入ClassLoader
对象的实例。 -
如果Bean实现了
BeanFactoryAware
接口,调用setBeanClassLoader()
方法,传入ClassLoade
r对象的实例。 -
与上面的类似,如果实现了其他
*.Aware
接口,就调用相应的方法。 -
如果有和加载这个 Bean 的 Spring 容器相关的
BeanPostProcessor
对象,执行postProcessBeforeInitialization()
方法 -
如果Bean实现了
InitializingBean
接口,执行afterPropertiesSet()
方法。 -
如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
-
如果有和加载这个 Bean的 Spring 容器相关的
BeanPostProcessor
对象,执行postProcessAfterInitialization()
方法 -
当要销毁 Bean 的时候,如果 Bean 实现了
DisposableBean
接口,执行destroy()
方法。 -
当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。
如何解决循环依赖问题?
DefaultSingletonBeanRegistry,该类中提供了三级缓存,通过提前暴露对象引用来达到解决循环依赖的问题。
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
singletonObjects中缓存的是经历了完整生命周期的bean对象。
earlySingletonObjects中缓存的是早期的Bean对象,早期指的是该bean对象的生命周期还没有走完。
singletonFactories中缓存的是ObjectFactory,表示对象工厂,是用来创建bean对象的。
首先就是对象a填充属性发现依赖b对象,然后发现无法获取到b对象,a完成实例化之后使用实例化后的对象创建一个对象工厂并放入三级缓存。此时创建b对象,发现b对象依赖a对象,此时可以从三级缓存中获取到a对象工厂,然后通过对象工厂获取a对象后将a对象放入二级缓存,并删除三级缓存,此时b会完成整个生命周期,此时将b注入a中,a再完成了整个生命周期就解决了循环依赖。
为什么使用三级缓存?而不是二级缓存或者一级缓存?
如果创建的
Bean
有对应的代理
,那其他对象注入时,注入的应该是对应的代理对象
;但是Spring
无法提前知道这个对象是不是有循环依赖
的情况,而正常情况
下(没有循环依赖
情况),Spring
都是在创建好完成品Bean之后才创建对应的代理。这时候Spring
有两个选择:1、不管有没有循环依赖,都提前创建好代理对象,并将代理对象放入缓存,出现循环依赖时,其他对象直接就可以取到代理对象并注入。
2、不提前创建好代理对象,在出现循环依赖被其他对象注入时,才实时生成代理对象。这样在没有循环依赖的情况下,Bean就可以按着Spring设计原则的步骤(使用后置处理器)来创建。
Spring选择了第二种方式,那怎么做到提前曝光对象而又不生成代理呢?
Spring就是在对象外面包一层ObjectFactory,提前曝光的是ObjectFactory对象,在被注入时才在ObjectFactory.getObject方式内实时生成代理对象,并将生成好的代理对象放入到第二级缓存Map<String, Object> earlySingletonObjects。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));