循环依赖

循环依赖指的是循环引用,也就是两个或两个以上的bean相互持有对方,最终形成闭环依赖。

比如:A依赖于B,B依赖于C,C又依赖于A。

spring中的循环依赖的场景有:

  • 构造器的循环依赖(spring中无法解决,只能避免)
  • 属性的循环依赖

如何检测循环依赖

就是在bean创建的时候,给bean打标,加上状态。如果在递归依赖的时候,发现依赖对象正处于创建中的状态,那么就说明循环依赖了。

spring怎么解决循环依赖

spring的循环依赖的理论依据其实在基于java的引用传递,当获取对象的引用的时候,对象的属性是可以延后设置的(但构造器必须是在获取引用之前)。

spring的单例对象的初始化:

  • createBeanInstance:实例化,调用对象的构造方法实例化对象
  • populateBean:填充属性,bean的依赖属性填充
  • InitializeBean:初始化,调用spring xml中的init方法

那么循环依赖主要发生在第一部分和第二部分。也就是构造器循环依赖和属性循环依赖。

spring为了解决单例的循环依赖问题,使用了三级缓存

1
2
3
4
5
6
7
8
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

三级缓存:

  • singletonFactories:单例对象工厂的cache
  • earlySingletonObjects:提前曝光的单例对象的cache
  • singletonObjects:单例对象的cache
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
  • isSingletonCurrentlyInCreation()判断当前单例bean是否正在创建中,也就是没有初始化完成(比如A的构造器依赖了B对象所以得先去创建B对象, 或则在A的populateBean过程中依赖了B对象,得先去创建B对象,这时的A就是处于创建中的状态。)
  • allowEarlyReference 是否允许从singletonFactories中通过getObject拿到对象

创建bean的时候,首先现从一级缓存singletonObjects中获取。如果获取不到,且对象正在创建,就从二级缓存earlySingletonObjects中进行获取。如果还是获取不到且有对象的创建工厂,就从三级缓存中获取。在不报错的情况下,三级缓存获取到后,会从三级缓存中移除,放入到二级缓存中。
三级缓存是ObjectFactory,定义如下:

1
2
3
public interface ObjectFactory<T> {
T getObject() throws BeansException;
}

addSingletonFactory发生在createBeanInstance之后,也就是说,单例对象已被创建了(调用了构造器)。虽然还没进行属性的赋值,但已经是一个对象了,可以根据应用在堆中定位到对象。放到二级缓存中提前让其他对象进行依赖和使用。由于java中对象的属性,可以在初始化之后进行赋值。所以这样一来,就解决属性的循环依赖问题。这也是无法解决构造器循环依赖的原因。在三级缓存面前,是先要调用构造器生成对象的。

1
2
3
4
5
6
7
8
9
10
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}