循环依赖

什么是循环依赖?

a对象中依赖b对象,b对象中依赖a对象,然后Spring在Bean对象属性填充时发现无法从IOC容器中发现已经实例化好的对象,因此无法完成自身的属性注入,因此bean生命周期终止,导致bean对象实例化失败。

Bean的生命周期

Bean生命周期

如何解决循环依赖问题?

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));