【面试】【4】循环依赖是什么,spring是怎么解决的

1、循环依赖在Spring中有哪几种情况?

  (1)通过构造方法进行依赖注入时产生的循环依赖问题。

  (2)通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。

  (3)通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。

 

2、只有第三种循环依赖可以解决,为什么其余两种不可以?

  第(1)种构造方法注入的情况下,在new对象的时候就会堵塞住了,其实也就是”先有鸡还是先有蛋“的历史难题。

  第(2)种setter方法(多例)的情况下,每一次getBean()时,都会产生一个新的Bean,如此反复下去就会有无穷无尽的Bean产生了,最终就会导致OOM问题的出现

 

3、通过setter方法进行依赖注入的单例模式是如何解决循环依赖问题的?

  通过二级缓存和三级缓存解决

 

4、如何通过缓存实现的呢?

  举个例子,A和B两个类发生循环依赖时:

  A的实例化过程:

  step1:创建A实例,实例化的时候将A对象工作放入了三级缓存,表示A开始实例化,虽然A对象还不完整,但是提前曝光出来让大家知道

  step2:A在注入属性的时候,发现依赖了B,但是B还没创建出来,这时候,就需要去实例化B啦

  step3:在B注入属性时发现依赖A,他会从缓存中找A对象。依次从一级缓存到三级缓存的查询A,直到在三级缓存中通过对象工厂找到A,A虽然还没完善,但是已经存在了,把A放入二级缓存,同时删除三级缓存中的A,此时B已经实例化且初始化完成,把B放入一级缓存

  step4:对A继续属性赋值,顺利从一级缓存拿到实例化且初始化完成的B对象,A对象创建完成,删除二级缓存中的A,同时将A放入一级缓存

  step5:最终,一级缓存中保存着实例化和初始化都完成的A、B对象

  以上过程也说明了为什么spring能解决setter注入的循环依赖,因为实例化和赋值是分开的,所以步骤中有操作的空间,如果都是构造器注入的话,都需要在实例化这一步骤中完成注入,自然就不行啦

 

5、单例Bean初始化完成的步骤

  实例化->属性赋值->初始化

 

6、注入就发生在属性赋值,结合这个过程,Spring 通过三级缓存解决了循环依赖:

  一级缓存 : Map<String,Object> singletonObjects,单例池,用于保存实例化、属性赋值(注入)、初始化完成的 bean 实例

  二级缓存 : Map<String,Object> earlySingletonObjects,早期曝光对象,用于保存实例化完成的 bean 实例

  三级缓存 : Map<String,ObjectFactory<?>> singletonFactories,早期曝光对象工厂,用于保存 bean 创建工厂,以便于后面扩展有机会创建代理对象

PS:开发人员在设计和落地代码的时候就避免循环依赖是最好的解决方法

posted @ 2022-08-17 15:15  青玄0316  阅读(69)  评论(0编辑  收藏  举报