Spring解决循环依赖
# 前言
在了解循环依赖前,需要对bean的创建流程十分熟悉,如果没有看过相关源码,可以参考:Spring源码--bean的创建流程 (opens new window)
# 1.什么是循环依赖
简单来说,就是A依赖B,B依赖C,C依赖A。 循环依赖的情况下,对象创建时,所依赖的对象并不存在,导致创建出现问题。 循环依赖有两种情况: 1、构造器循环依赖
public class D {
public D(C c) {
...
}
}
public class C {
public C(D d) {
...
}
}
2、属性循环依赖
public class A {
private B b;
}
public class B {
private A a;
}
# 2.Spring如何解决循环依赖
Spring通过三级缓存来解决循环依赖问题
// 一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// 三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
一级缓存:存放的是完整单例对象,状态为实例化完成、属性注入完成、初始化完成 二级缓存:存放的是提前暴露的单例对象,状态为创建中,实例化完成,但未初始化 三级缓存:存放的是bean工厂,此时对象(指目标单例对象)状态为创建中,实例化但未初始化
- doCreateBean()
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// *******************<1>实例化********************
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
// 尝试从缓存获取
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 通过构造方法创建bean实例
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
...
// 后置处理器修改bean定义信息
...
// ********************<3>解决循环依赖********************
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
...
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// ********************<3>初始化********************
Object exposedObject = bean;
try {
// 根据配置文件value值 填充属性
populateBean(beanName, mbd, instanceWrapper);
// 初始化方法
// 前置-initmethod - 后置
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
...
return exposedObject;
}
从缓存获取单例
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从一级缓存中获取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 如果一级缓存里没有 且 bean正在创建中
synchronized (this.singletonObjects) {
// 从二级缓存里获取
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 二级缓存没有 从三级缓存获取一个工厂
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 能获取到工厂 则创建bean
singletonObject = singletonFactory.getObject();
// 把实例存入二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 把工厂从三级缓存移除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
- addSingletonFactory()
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
// 如果一级缓存没值,则把beanFactory存入三级缓存
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
- addSingleton()
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 存入一级缓存
this.singletonObjects.put(beanName, singletonObject);
// 清空三级缓存
this.singletonFactories.remove(beanName);
// 清空二级缓存
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
正常不存在循环依赖的A、B对象是依次创建的,但是如果存在循环依赖的话,创建A的过程中,会顺便把B也创建了。 注意,每次获取bean对象都会先去一级缓存看有没有值。 具体流程是: 1、遍历待创建beanName,第一次遍历,开始获取A,此时缓存中没有,会开始正常创建流程 2、A实例化完成,进入步骤<2>处理循环依赖,调用addSingletonFactory(),它会判断此时一级缓存没有A,则把A的beanFactory存入三级缓存 3、A开始填充b属性,,于是调用beanFactory.getBean()获取B,往里继续从缓存获取B,获取不到,则开始正常创建B的流程 4、B实例化完成,同样进入步骤<2>处理循环依赖,调用addSingletonFactory(),它会判断此时一级缓存没有B,则把B的beanFactory存入三级缓存 5、B开始填充a属性,于是调用beanFactory.getBean()获取A,判断此时一级缓存没有A,但不同的是,此时A处于创建中,在上文getSingleton() 方法中,isSingletonCurrentlyInCreation()结果为true,则往下走,从二级获取,此时二级缓存为空,当然是获取不到,继续从三级缓存获取beanFactory,从工厂获取到A。接下来把A存入二级缓存,清除三级缓存。因为此时能获取到A,所以B的a属性能填充成功,B接着执行初始化,B处于实例化、初始化都完成的完全状态。 6、B执行addSington(),把完全状态的B存入一级缓存,清空二三级缓存(实际只有三级有值) 7、A开始填充b属性,于是调用beanFactory.getBean()获取B,第六步已经把B存入一级缓存,此时直接返回,填充成功,继续执行初始化,得到一个完全状态的A 8、A执行addSington(),把完全状态的A存入一级缓存,清空二三级缓存(实际只有二级有值) 9、第二次遍历,开始获取B,此时一级缓存中有B,直接返回。
至此A、B全部实例化、初始化完成
# 3.问题与总结
Spring解决循环依赖的关键在于,提前通过beanFactory提前曝光对象,这样不用等对象完全创建成功,就能作为其他对象的属性注入。 由上述流程可知,Spring只能解决属性的循环依赖,而无法解决构造器循环依赖。因为bean实例化本身就需要通过构造器完成,后面的加入三级缓存处理循环依赖自然无法完成。
- 问题1:只用一级缓存可以吗?
答:不能。一级缓存存放的是创建完成的对象,而二级缓存存放的是创建中的对象,如果只用一级缓存,那么没法把两种状态对象区分开。至少需要用两级缓存
- 问题2:只用一二级缓存可以吗?
答:不能。所需要的对象可能是简单对象,也可能是代理对象。所以三级缓存里存入beanFactory,在获取的时候决定它是简单对象还是代理对象。所以这样可以在需要的时候生成对象,而不是提前生成。