Spring的循环依赖指的是两个或以上的bean对象互相作为对方的属性,例如A类中存在属性B,B类中存在属性A。Spring在创建实例化A时发现需要注入B类,就先去创建B类,在创建B类时发现需要注入A类,这时候又去寻找A类。这就是循环依赖,如下图:
如果按照Spring普通的方式去进行实例化,是不可避免循环依赖的。我们从源码开始,一步一步分析Spring是如何解决循环依赖的。首先是从获取bean开始。我们从doGetBean方法开始进入,注意这个getSingleton(beanName),顾名思义就是根据beanName获取一个单例对象。假如我现在是在获取A类的Bean,那么这里就是通过A的beanName获取bean A。点击进入方法。
这里包括源码的注释和翻译我一起贴出来
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从单例缓存中获取单例对象
Object singletonObject = this.singletonObjects.get(beanName);
// 如果singletonObject为空,并且对象正在创建中,就进行下一步
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) {
// 调用getObject()方法创建一个单例对象
singletonObject = singletonFactory.getObject();
// 放入二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 从单例工厂中移除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
首先是从一级singletonObjects中获取bean,这个singletonObjects是一个存放创建完毕的Bean的Map。很明显A正在创建中,肯定获取不到。返回上一步,如果是正在创建的bean的作用域是原型模式,那么就直接抛异常
但是在实例化之前,Spring会将A类标记为正在创建中的bean,存入一个集合singletonsCurrentlyInCreation中,方便后面处理循环依赖的bean。在DefaultSingletonBeanRegistry类中有一个方法,顾名思义就是在单例创建之前执行的方法。
// 单例创建之前的回调。默认实现将单例注册为当前正在创建。 protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } }
接下来就是去创建bean A。Spring中一般做事的都是do开头的,所以直接去寻找doCreateBean去实例化A。具体的实例化过程我就不写了,关键在doCreateBean方法中这段代码,如果A类是单例、允许循环依赖、并且是正在创建中的单例。就将bean 存入缓存中,
这个addSingletonFactory函数式接口的作用就是将bean存入三级缓存。方便其他bean需要注入该bean的时候可以直接获取
到这里A类就实例化完毕,在为A注入属性的时候九华发现B类需要注入,Spring就会去创建B类的Bean。再次进入我们第一次贴的getSingleton(beanName)方法时,就可以获取到A的bean了。通过我们存入三级缓存的对象工厂直接拿到对象A。B创建完毕后A也能注入了。大致的流程如下图:



