下面我们来一个个突破。
什么是循环依赖?
这个很好理解,多个bean之间相互依赖,形成了一个闭环。
比如:A依赖于B、B依赖于C、C依赖于A。
代码中表示:
public class A{
B b;
}
public class B{
C c;
}
public class C{
A a;
}
如何检测是否存在循环依赖?
检测循环依赖比较简单,使用一个列表来记录正在创建中的bean,bean创建之前,先去记录中看一下自己是否已经在列表中了,如果在,说明存在循环依赖,如果不在,则将其加入到这个列表,bean创建完毕之后,将其再从这个列表中移除。
源码方面来看一下,spring创建单例bean时候,会调用下面方法
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
singletonsCurrentlyInCreation
就是用来记录目前正在创建中的bean名称列表,this.singletonsCurrentlyInCreation.add(beanName)
返回false
,说明beanName已经在当前列表中了,此时会抛循环依赖的异常BeanCurrentlyInCreationException
,这个异常对应的源码:
public BeanCurrentlyInCreationException(String beanName) {
super(beanName,
“Requested bean is currently in creation: Is there an unresolvable circular reference?”);
}
上面是单例bean检测循环依赖的源码,再来看看非单例bean的情况。
以prototype情况为例,源码位于org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
方法中,将主要代码列出来看一下:
//检查正在创建的bean列表中是否存在beanName,如果存在,说明存在循环依赖,抛出循环依赖的异常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
//判断scope是否是prototype
if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
//将beanName放入正在创建的列表中
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
//将beanName从正在创建的列表中移除
afterPrototypeCreation(beanName);
}
}
Spring如何解决循环依赖的问题
这块建议大家先看一下:详解bean生命周期
spring创建bean主要的几个步骤:
-
步骤1:实例化bean,即调用构造器创建bean实例
-
步骤2:填充属性,注入依赖的bean,比如通过set方式、@Autowired注解的方式注入依赖的bean
-
步骤3:bean的初始化,比如调用init方法等。
从上面3个步骤中可以看出,注入依赖的对象,有2种情况:
-
通过步骤1中构造器的方式注入依赖
-
通过步骤2注入依赖
先来看构造器的方式注入依赖的bean,下面两个bean循环依赖
@Component
public class ServiceA {
private ServiceB serviceB;
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Component
public class ServiceB {
private ServiceA serviceA;
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
构造器的情况比较容易理解,实例化ServiceA的时候,需要有serviceB,而实例化ServiceB的时候需要有serviceA,构造器循环依赖是无法解决的,大家可以尝试一下使用编码的方式创建上面2个对象,是无法创建成功的!
再来看看非构造器的方式注入相互依赖的bean,以set方式注入为例,下面是2个单例的bean:serviceA和serviceB:
@Component
public class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Component
public class ServiceB {
private ServiceA serviceA;
@Autowired
public void setServiceA(ServiceA serviceA) {
this.serviceA = serviceA;
}