在Spring中,两个或多个Bean互相持有对方,最终反映为一个环,就构成了循环依赖。如:
public class A {
@Autowired
private B b;
}
public class B {
@Autowired
private A a;
}
构造器循环依赖(不可解决)
setter循环依赖(可以解决)
prototype范围的循环依赖(不可解决)
在DefaultSingletonBeanRegistry中,Spring定义了三个map,这就是我们平时说的三级缓存
// 一级缓存:用于存放完全初始化完成的bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 三级缓存:存放bean工厂对象
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 二级缓存:用于存放原始的bean对象(尚未填充属性)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 尝试在singletonObjects中获取bean
Object singletonObject = this.singletonObjects.get(beanName);
// 如果没有获取到,并且该bean在创建中,
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 尝试在earlySingletonObjects获取bean
singletonObject = this.earlySingletonObjects.get(beanName);
// 还是没有获取到,并且允许创建早期对象的引用
if (singletonObject == null && allowEarlyReference) {
// 因为需要操作singletonObjects,所以将其锁定
synchronized (this.singletonObjects) {
// 再次尝试获取
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 在singletonFactories中获取其bean工厂
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 如果获取到bean工厂,通过工厂的getObject()方法获取其早期实例
singletonObject = singletonFactory.getObject();
// 之后将其早期实例存放到earlySingletonObjects中,也就是将三级缓存提高到了二级
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
// 返回实例
return singletonObject;
}
isSingletonCurrentlyInCreation()方法用于判断当前单例bean是否正在创建中

IOC大致流程:
1.getBean -> 2.第一个getSingleton(在三级缓存取bean) -> 3.第二个getSingleton(去创造bean) -> 4.beforeSingletonCreation(向singletonsCurrentlyInCreation中添加正在创建的bean) -> 5.createBean -> 6.doCreateBean(从上至下调用的方法为:createBeanInstance,addSingletonFactory放入三级缓存中,populateBean填充属性,initializeBean初始化bean) -> 7.放入单例池中
如果是构造器注入的话(假如有A、B类,A先B后),A第一次先把自己放入singletonsCurrentlyInCreation中,然后在createBeanInstance时会去调用@AutoWired标注的有参构造器(此时A没有实例化,连对象都没创建),然后会去getBean(B),这就回到了上方流程的开头,B在第一个getSingleton没有获取到A,然后就去getBean(A),对于A来说已经是第二次了,于是在向singletonsCurrentlyInCreation添加的时候就会报错,因为该集合已经有了A,因此异常在此处抛出。

对于prototype范围的bean,Spring每次都会创建一个新的实例,该实例不会被缓存,因此不能提前暴露一个创建中的bean到缓存中,也就无法解决
使用三级缓存主要针对的不是IOC,而是AOP。如果 Spring 选择二级缓存来解决循环依赖的话,那么就意味着所有 Bean 都需要在实例化完成之后就立马为其创建代理,而 Spring 的设计原则是在 Bean 初始化完成之后才为其创建代理。
即:若使用二级缓存,在 AOP 情形注入到其他 Bean的,不是最终的代理对象,而是原始对象。