循环依赖
循环依赖指的是就是两个或多个bean相互之间的引用,A引用B,B引用C,C又引用A,最终形成一个循环。
分类
根据循环依赖出现的情况,可以分为构造器循环依赖和属性setter循环依赖。
-
构造器循环依赖
Spring无法解决这类循环依赖。原因是这种情况下,Spring 容器没办法决定先创建哪一个 bean。这时候会抛出异常 BeanCurrentlyInCreationException
-
单例的setter注入循环依赖
Spring 通过三级缓存,提前暴露创建 Bean 的 ObjectFactory 解决 Setter 循环依赖。后面具体说明这个过程。
-
原型的setter注入循环依赖
由于 prototype 作用域的bean,Spring容器每次都会生成一个新的对象,所以没办法通过类似单例的方法解决循环依赖问题。同样会出现Spring 容器没办法决定先创建哪一个 bean的问题。
Spring解决循环依赖的三级缓存
所谓的三级缓存,实际上指的是在Spring创建bean的三个步骤中分别缓存实例化对象。
简单地,以A依赖B,B依赖A为例,当实例化A时,我们通过调用doGetBean(“A”)来完成。在A对象实例化完成后,Spring会将该对象提前暴露出去(存放在一个Map中)。
接着,当进行属性填充时,发现A依赖了B的实例。这时候,Spring会调用doGetBean(“B”)来实例化B对象。
同样地,B对象实例化完成后,也会被提前暴露出去,存放在Map中。然后,继续填充B对象的属性,发现B依赖了A。这时,Spring不会重新实例化A对象,而是从之前提前暴露的Map中获取已经存在的A对象。
通过这种方式,B对象的属性填充完成,可以执行接下来的初始化过程。当B对象初始化完成后,再回到A对象的属性填充阶段,此时可以获取到B对象,并成功完成属性填充。
这样,Spring巧妙地解决了循环依赖的问题。通过提前暴露对象并利用Map进行缓存,避免了重复实例化的情况,确保了循环依赖的正确处理。
流程图如下:
Spring相关源码
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//首先去一级缓存中获取如果获取的到说明bean已经存在,直接返回
Object singletonObject = this.singletonObjects.get(beanName);
//如果一级缓存中不存在,则去判断该bean是否在创建中,如果该bean正在创建中,就说明了,这个时候发生了循环依赖
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//如果发生循环依赖,首先去二级缓存中获取,如果获取到则返回,这个地方就是获取aop增强以后的bean
singletonObject = this.earlySingletonObjects.get(beanName);
//如果二级缓存中不存在,且允许提前访问三级引用
if (singletonObject == null && allowEarlyReference) {
//去三级缓存中获取
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//如果三级缓存中的lambda表达式存在,执行aop,获取增强以后的对象,为了防止重复aop,将三级缓存删除,升级到二级缓存中
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}