在Spring中,对象不是new出来的,而是经历过一系列生命周期,通过依赖注入最终获得的。 循环依赖就是指多个bean之间的互相依赖,或者是自己注入自己的情况下,就比如A依赖于B,B依赖于C,C又依赖于A,这样就创造出了一个类似于先有鸡还是先有蛋的问题,造成系统崩溃。
Spring的循环依赖多发生在Spring容器注入的时候,且是单例。
从根本问题出发,打破循环,不让Bean循环!
那么Spring是怎么样实现的呢?
首先,单例Bean的初始化完成过程为有三个阶段
①实例化;②属性赋值,③初始化
我们刚才所说的Spring容器注入主要发生在第①步初始化和②步属性赋值。所以我们要切入这个点去解决问题。
Spring解决单例循环依赖问题,使用了三级缓存
三级缓存:
/** Cache of singleton objects: bean name –> bean instance */
private final Map singletonObjects = new ConcurrentHashMap(256);
/** Cache of singleton factories: bean name –> ObjectFactory */
private final Map> singletonFactories = new HashMap>(16);
/** Cache of early singleton objects: bean name –> bean instance */
private final Map earlySingletonObjects = new HashMap(16);
每级缓存的大致意思作用
singletonFactories : 进入实例化阶段的单例对象工厂的cache (三级)
earlySingletonObjects :完成实例化但是尚未初始化的,提前暴光的单例对象的Cache (二级)
singletonObjects:完成初始化的单例对象的cache(一级)
我们创建Bean的时候,会先从cache缓存里找,这一部其实就是singletonObjects,它里面的方法用到了三级缓存。
来看看他的主要方法
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
//判断该单例Bean是否正在创建
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
//是否允许从singletonFactories中通过getObject拿到对象
if (singletonObject == null && allowEarlyReference) {
ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
//将Bean放入earlySingletonObjects中,并从singletonFactories中移除,。
//从三级缓存过渡到了二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
Spring解决依赖循环主要就是靠这个三级缓存。
我们来分析一下
“A的setter依赖了B的实例对象,同时B的setter依赖了A的实例对象”这种循环依赖的情况。
A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,A发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走B的create流程。
B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(没有,此时A还没初始化完全),尝试二级缓存earlySingletonObjects(还是没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是也可以获取到,虽然用不了),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。
返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,
最后由于B拿到了A的对象引用,所以B现在也能获取到A对象,并完成了初始化。
最后说一下并不是所有单例Spring都能解决以来循环。
可以解决的单例:①都是setter注入,②都是属性注入,③一个B是setter注入,一个A是构造器注入(A中注入B(setter),B中注入A(构造器))
不能解决的单例:①都是构造器注入,②一个A是setter注入,一个B是构造器注入(B中注入A,A中注入B)
原因是 Spring 在创建 Bean 时默认会根据自然排序进行创建,A 会先于 B 进行创建。