欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring的循环依赖

程序员文章站 2024-03-23 12:50:04
...

转载:https://www.cnblogs.com/zzq6032010/p/11406405.html

总结:

循环依赖可以分为二种:

1.构造器的循环依赖

在二个service的实现类中互相注入,互为构造器的参数,这种情况无法解决,因为JVM虚拟机在对类进行实例化的时候,需先实例化构造器的参数,而由于循环引用这个参数无法提前实例化。

2.属性的循环依赖

在二个service的实现类中互相注入,但是没有互为构造器的参数,这种情况是可以正常实例化的,但是如果加上了@scope=prototype的话就不能正常实例化了,默认是单例,是可以正常实例化了,实例化就类似于new 一个对象的过程。

参考:https://www.jianshu.com/p/8bb67ca11831,如果想要详细地去了解的话,可以去原文章处了解。

再做进一步总结:

1、构造器注入和prototype类型的field注入发生循环依赖时都无法初始化

2、属性注入单例的bean时,尽管有循环依赖,但bean仍然可以被成功初始化

面试官可能会提的问题

  1. 单例的属性注入bean是如何解决循环依赖问题呢?如果A中注入了B,那么他们初始化的顺序是什么样子的?
  2. 为什么prototype类型的和构造器类型的Spring无法解决循环依赖呢?

对于第一个问题:

我们假设循环注入是:A依赖B(A中autowire了B),B又依赖A(B中又autowire了A),那么它的的指向顺序是这样的

 解决循环依赖的关键方法,getSingleton,addSingletonFactory

第一个方法getSingleton会从singletonFactories里面拿Singleton,而addSingletonFactory会把Singleton放入singletonFactories

Spring的循环依赖

 总结起来就是

Spring在InstantiateBean时执行构造器方法,构造出实例,如果是单例的话,会将它放入一个singletonBeanFactory的缓存中,再进行populateBean方法,设置属性。通过一个singletonBeanFactory的缓存解决了循环依赖的问题

 其实就是利用三级缓存来解决问题的,也就是singletonBean的生产工厂,在DefaultListableBeanFactory类中的三个map,

一级缓存:
/** 保存所有的singletonBean的实例 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);

二级缓存:
/** 保存所有早期创建的Bean对象,这个Bean还没有完成依赖注入 */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
三级缓存:
/** singletonBean的生产工厂*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
 

 如果A注入了B,它们的初始化顺序为:

单例的设值注入,如果A中注入了B,B应该是A中的一个属性,那么猜想应该是A已经被instantiate(实例化)之后,在populateBean(填充A中的属性)时,对B进行初始化

原因是:

因为A中构造器注入了B,那么A在关键的方法addSingletonFactory()之前就去初始化了B,导致三级缓存中根本没有A,所以会发生死循环,Spring发现之后就抛出异常了。至于Spring是如何发现异常的呢,本质上是根据Bean的状态给Bean进行mark,如果递归调用时发现bean当时正在创建中,那么久抛出循环依赖的异常即可。

对于第二个问题:

需要先了解prototype的Bean是如何初始化的。prototypeBean有一个关键的属性,是被final修饰的ThreadLocal,

保存着正在创建的prototype的beanName,在流程上并没有暴露任何factory之类的缓存。并且在beforePrototypeCreation(String beanName)方法时,把每个正在创建的prototype的BeanName放入一个set中,并且会循环依赖时检查beanName是否处于创建状态,如果是就抛出异常,因为无论是构造注入还是设值注入,第二次进入同一个Bean的getBean方法是,一定会在校验部分抛出异常,因此不能完成注入,也就不能实现循环引用。

相关标签: 面试总结