1. 简介
循环依赖相关的源码本身不是很复杂,不过这里要先介绍大量的前置知识。不然这些源码看起来很简单,但读起来可能却也不知所云。那下面我们先来了解一下什么是循环依赖。
2. 背景知识
2.1 什么是循环依赖
所谓的循环依赖是指,A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。或者是 A 依赖 B,B 依赖 C,C 又依赖 A。它们之间的依赖关系如下:
这里以两个类直接相互依赖为例,他们的实现代码可能如下:
public class BeanB {
private BeanA beanA;
// 省略 getter/setter
}
public class BeanA {
private BeanB beanB;
} 配置信息如下:
<bean id="beanA" class="xyz.coolblog.BeanA"> <property name="beanB" ref="beanB"/> </bean> <bean id="beanB" class="xyz.coolblog.BeanB"> <property name="beanA" ref="beanA"/> </bean>
IOC 容器在读到上面的配置时,会按照顺序,先去实例化 beanA。然后发现 beanA 依赖于 beanB,接在又去实例化 beanB。实例化 beanB 时,发现 beanB 又依赖于 beanA。如果容器不处理循环依赖的话,容器会无限执行上面的流程,直到内存溢出,程序崩溃。当然,Spring 是不会让这种情况发生的。在容器再次发现 beanB 依赖于 beanA 时,容器会获取 beanA 对象的一个早期的引用(early reference),并把这个早期引用注入到 beanB 中,让 beanB 先完成实例化。beanB 完成实例化,beanA 就可以获取到 beanB 的引用,beanA 随之完成实例化。这里大家可能不知道“早期引用”是什么意思,这里先别着急,我会在下一章进行说明。
好了,本章先到这里,我们继续往下看。
2.2 一些缓存的介绍
/** 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);
| 缓存 | 用途 |
|---|---|
| singletonObjects | 用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用 |
| earlySingletonObjects | 提前曝光的单例cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖 |
| singletonFactories | 存放 bean 工厂对象,用于解决循环依赖 |
/** Room 包含了一些电器 */
public class Room {
private String television;
private String airConditioner;
private String refrigerator;
private String washer;
// 省略 getter/setter
} <bean id="room" class="xyz.coolblog.demo.Room"> <property name="television" value="Xiaomi"/> <property name="airConditioner" value="Gree"/> <property name="refrigerator" value="Haier"/> <property name="washer" value="Siemens"/> </bean>
从调试信息中可以看得出,Room 的每个成员变量都被赋上值了。然后我们再来看一下“原始的 bean 对象”长的是什么样的,如下:
结果比较明显了,所有字段都是 null。这里的 bean 和上面的 bean 指向的是同一个对象Room@1567,但现在这个对象所有字段都是 null,我们把这种对象成为原始的对象。形象点说,上面的 bean 对象是一个装修好的房子,可以拎包入住了。而这里的 bean 对象还是个毛坯房,还要装修一下(填充属性)才行。
2.3 回顾获取 bean 的过程
本节,我们来了解从 Spring IOC 容器中获取 bean 实例的流程(简化版),这对我们后续的源码分析会有比较大的帮助。先看图:
- createBeanInstance:例化,其实也就是调用对象的构造方法实例化对象
- populateBean:填充属性,这一步主要是对bean的依赖属性进行注入(@Autowired)
- initializeBean:回到一些形如initMethod、InitializingBean等方法
我来按照上面的图,分析一下整个流程的执行顺序。这个流程从 getBean 方法开始,getBean 是个空壳方法,所有逻辑都在 doGetBean 方法中。doGetBean 首先会调用 getSingleton(beanName) 方法获取 sharedInstance,sharedInstance 可能是完全实例化好的 bean,也可能是一个原始的 bean,当然也有可能是 null。如果不为 null,则走绿色的那条路径。再经 getObjectForBeanInstance 这一步处理后,绿色的这条执行路径就结束了。
我们再来看一下红色的那条执行路径,也就是 sharedInstance = null 的情况。在第一次获取某个 bean 的时候,缓存中是没有记录的,所以这个时候要走创建逻辑。上图中的 getSingleton(beanName, new ObjectFactory<Object>() {...}) 方法会创建一个 bean 实例,上图虚线路径指的是 getSingleton 方法内部调用的两个方法,其逻辑如下:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
// 省略部分代码
singletonObject = singletonFactory.getObject();
// ...
addSingleton(beanName, singletonObject);
} 我这里没有把 getObject、addSingleton 方法和 getSingleton(String, ObjectFactory) 并列画在红色的路径里,目的是想简化一下方法的调用栈(都画进来有点复杂)。我们可以进一步简化上面的调用流程,比如下面:
这个流程看起来是不是简单多了,命中缓存走绿色路径,未命中走红色的创建路径。好了,本节先到这。
3. 源码分析
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
// ......
// 从缓存中获取 bean 实例
Object sharedInstance = getSingleton(beanName);
// ......
}
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从 singletonObjects 获取实例,singletonObjects 中的实例都是准备好的 bean 实例,可以直接使用
Object singletonObject = this.singletonObjects.get(beanName);
// 判断 beanName 对应的 bean 是否正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 从 earlySingletonObjects 中获取提前曝光的 bean
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 获取相应的 bean 工厂
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 提前曝光 bean 实例(raw bean),用于解决循环依赖
singletonObject = singletonFactory.getObject();
// 将 singletonObject 放入缓存中,并将 singletonFactory 从缓存中移除
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
} - 先从一级缓存singletonObjects中去获取。(如果获取到就直接return)
- 如果获取不到或者对象正在创建中(isSingletonCurrentlyInCreation()),那就再从二级缓存earlySingletonObjects中获取。(如果获取到就直接return)
- 如果还是获取不到,且允许singletonFactories(allowEarlyReference=true)通过getObject()获取。就从三级缓存singletonFactory.getObject()获取。(如果获取到了就从singletonFactories中移除,并且放进earlySingletonObjects。其实也就是从三级缓存移动(是剪切、不是复制哦~)到了二级缓存)
getSingleton()从缓存里获取单例对象步骤分析可知,Spring解决循环依赖的诀窍:就在于singletonFactories这个三级缓存。这个Cache里面都是ObjectFactory,它是解决问题的关键。
此处说一下二级缓存earlySingletonObjects它里面的数据什么时候添加什么移除???
- 添加:向里面添加数据只有一个地方,就是上面说的getSingleton()里从三级缓存里挪过来
- 移除:addSingleton、addSingletonFactory、removeSingleton从语义中可以看出添加单例、添加单例工厂ObjectFactory的时候都会删除二级缓存里面对应的缓存值,是互斥的
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
// ......
Object bean;
// 从缓存中获取 bean 实例
Object sharedInstance = getSingleton(beanName);
// 这里先忽略 args == null 这个条件
if (sharedInstance != null && args == null) {
// 进行后续的处理
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
// ......
// mbd.isSingleton() 用于判断 bean 是否是单例模式
if (mbd.isSingleton()) {
// 再次获取 bean 实例
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
// 创建 bean 实例,createBean 返回的 bean 是完全实例化好的
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
// 进行后续的处理
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// ......
}
// ......
// 返回 bean
return (T) bean;
} public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
// ......
// 调用 getObject 方法创建 bean 实例
singletonObject = singletonFactory.getObject();
newSingleton = true;
if (newSingleton) {
// 添加 bean 到 singletonObjects 缓存中,并从其他集合中将 bean 相关记录移除
addSingleton(beanName, singletonObject);
}
// ......
// 返回 singletonObject
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 将 <beanName, singletonObject> 映射存入 singletonObjects 中
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
// 从其他缓存中移除 beanName 相关映射
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
} protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
// ......
// 创建 bean 对象,并将 bean 对象包裹在 BeanWrapper 对象中返回
instanceWrapper = createBeanInstance(beanName, mbd, args);
// 从 BeanWrapper 对象中获取 bean 对象,这里的 bean 指向的是一个原始的对象
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
/*
* earlySingletonExposure 用于表示是否”提前暴露“原始对象的引用,用于解决循环依赖。
* 对于单例 bean,该变量一般为 true。更详细的解释可以参考我之前的文章
*/
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 添加 bean 工厂对象到 singletonFactories 缓存中
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
/*
* 获取原始对象的早期引用,在 getEarlyBeanReference 方法中,会执行 AOP
* 相关逻辑。若 bean 未被 AOP 拦截,getEarlyBeanReference 原样返回
* bean,所以大家可以把
* return getEarlyBeanReference(beanName, mbd, bean)
* 等价于:
* return bean;
*/
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
Object exposedObject = bean;
// ......
// 填充属性,解析依赖
populateBean(beanName, mbd, instanceWrapper);
// ......
// 返回 bean 实例
return exposedObject;
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
// 将 singletonFactory 添加到 singletonFactories 缓存中
this.singletonFactories.put(beanName, singletonFactory);
// 从其他缓存中移除相关记录,即使没有
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
} 1. 创建原始 bean 实例 createBeanInstance(beanName, mbd, args)
2. 添加原始对象工厂对象到 singletonFactories 缓存中
addSingletonFactory(beanName, new ObjectFactory<Object>{...})
3. 填充属性,解析依赖 populateBean(beanName, mbd, instanceWrapper) instanceWrapper = createBeanInstance(beanName, mbd, args); final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
}); 3. 解析依赖
populateBean(beanName, mbd, instanceWrapper);
populateBean 用于向 beanA 这个原始对象中填充属性,当它检测到 beanA 依赖于 beanB 时,会首先去实例化 beanB。beanB 在此方法处也会解析自己的依赖,当它检测到 beanA 这个依赖,于是调用 BeanFactry.getBean("beanA") 这个方法,从容器中获取 beanA。
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) {
// 从 SingletonFactory 中获取早期引用
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
} 以上的过程对应下面的流程图:
为了完成依赖注入B,会通过getBean(B)去容器内找B。但此时B在容器内不存在,就走向B的创建之路~
实例化B,并将其放入缓存。(此时B也能够被引用了)
初始化B,@Autowired依赖注入A(此时需要去容器内获取A)
此处重要:初始化B时会调用getBean(A)去容器内找到A,上面我们已经说过了此时候因为A已经实例化完成了并且放进了缓存里,所以这个时候去看缓存里是已经存在A的引用了的,所以getBean(A)能够正常返回
B初始化成功(此时已经注入A成功了,已成功持有A的引用了),return(注意此处return相当于是返回最上面的getBean(B)这句代码,回到了初始化A的流程中~)。
因为B实例已经成功返回了,因此最终A也初始化成功
到此,B持有的已经是初始化完成的A,A持有的也是初始化完成的B,完美~
到这里,本篇文章差不多就快写完了,不知道大家看懂了没。这篇文章在前面做了大量的铺垫,然后再进行源码分析。相比于我之前写的几篇文章,本篇文章所对应的源码难度上比之前简单一些。但说实话也不好写,我本来只想简单介绍一下背景知识,然后直接进行源码分析。但是又怕有的朋友看不懂,所以还是用了大篇幅介绍的背景知识。这样写,可能有的朋友觉得比较啰嗦。但是考虑到大家的水平不一,为了保证让大家能够更好的理解,所以还是尽量写的详细一点。本篇文章总的来说写的还是有点累的,花了一些心思思考怎么安排章节顺序,怎么简化代码和画图。如果大家看完这篇文章,觉得还不错的话,不妨给个赞吧,也算是对我的鼓励吧。

京公网安备 11010502036488号