spring如何解决bean的循环依赖??

发布时间 2023-10-22 22:16:06作者: jishaleng
// 一级缓存:创建好的bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

// 三级缓存:解决循环依赖问题,ObjectFactory函数式接口,可以保障职责单一原则
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

// 二级缓存:解决循环依赖单例性,并发性能问题
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

1、如果只是解决死循环栈溢出问题,只需要在一级缓存即可,在实例化前判断一级缓存是否有bean,没有就在实例化对象后加入一级缓存,这样就可解决死循环问题,但会存在线程不安全问题。

2、在多线程时候,会出现拿到只是实例化的bean,不是完整的bean,而报错。只需要判断一级缓存前,对一级缓存加同步锁,但这会带来并发性能低问题,假设线程A正在创建bean,而线程B只是想去一级缓存中拿已经创建好的bean,但因为一级缓存被加了同步锁,线程B就会被阻塞。

3、引入二级缓存解决并发性能低问题。将同步锁放置在实例化对象加入二级缓存前,并在判断一级缓存是否有bean的后面,对判断二级缓存是否有bean进行加同步锁,bean创建好后加入一级缓存,删除二级缓存中只是实例化的bean。

总共加了两个同步锁,判断bean时候对二级缓存加同步锁,创建bean的过程加同步锁。

4、但在多线程环境下,两个线程获取同一个bean时候,又会出现重复创建bean问题。线程A首先进入判断二级缓存是否有bean的代码块,而线程B也到了这个地方,但线程A获取了同步锁,线程B被阻塞,线程A创建好bean后,将bean放入一级缓存,删除二级缓存,释放锁。此时,线程B进入判断二级缓存是否有bean,因为线程A将bean加入一级缓存的同时删除了bean在二级缓存的实例化bean。因此线程B需要再次走创建bean的过程,带来了重复创建bean问题,为此需要在实例化bean前,再次判断一级缓存是否存在bean,这样线程B就可以直接获取bean。

双重检查锁:总共进行了两次判断一级缓存,在判断一级缓存时候判断了一次,在实例化bean前又判断了一次一级缓存是否存在bean,避免并发时候获取不完整的bean

5、因此bean最后是放在一级缓存的,即使A依赖B,B依赖A中先创建B的bean,B中只是一个实例化的bean A , 但Bean创建完后,会继续创建bean A,解决循环依赖问题。

6、bean的初始化阶段有bean前置处理器和bean后置处理器,动态代理是在bean后置处理器中的AbstractAutoProxyCreator抽象类操作的。普通的bean创建的动态代理是在初始化阶段完成;而循环依赖的bean可以在实例化后属性注入前创建动态代理对象,并放入二级缓存中,属性注入后再获取动态代理对象放入一级缓存中。

7、但为了保持动态代理对象在初始化阶段,更改为初始化阶段创建动态代理对象,当出现多次循环依赖时,即A-B相互依赖同时A-C相互依赖,会多次在初始化阶段创建A的代理对象,为此引入三级缓存,C依赖A时候,只需要从三级缓存中拿A的代理对象即可,不需要再次创建A的动态代理对象。

8、循环依赖发生在属性注入阶段,而动态代理发生在初始化阶段,当出现多次循环依赖时候,只使用二级缓存的话,会多次创建动态代理对象,因此引入三级缓存只需要创建一次代理对象即可