循环依赖
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。点击进入方法。
这里包括源码的注释和翻译我一起贴出来
/**
* Return the (raw) singleton object registered under the given name.
* <p>Checks already instantiated singletons and also allows for an early
* reference to a currently created singleton (resolving a circular reference).
* @param beanName the name of the bean to look for
* @param allowEarlyReference whether early references should be created or not
* @return the registered singleton object, or {@code null} if none found
*/
/**
* 返回在给定名称下注册的(原始)单例对象。 <p>检查已经实例化的单例,并允许提前引用当前创建的单例(解决循环引用)。
*/
@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类中有一个方法,顾名思义就是在单例创建之前执行的方法。
/**
* Callback before singleton creation.
* <p>The default implementation register the singleton as currently in creation.
* @param beanName the name of the singleton about to be created
* @see #isSingletonCurrentlyInCreation
*/
// 单例创建之前的回调。 <p>默认实现将单例注册为当前正在创建。
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也能注入了。大致的流程如下图: