Spring 循环引用源码分析
Posted binarylei
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring 循环引用源码分析相关的知识,希望对你有一定的参考价值。
Spring 循环引用(二)源码分析
Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)
Spring 循环引用相关文章:
- 《Spring 循环引用(一)一个循环依赖引发的 BUG》:https://www.cnblogs.com/binarylei/p/10325698.html
- 《Spring 循环引用(二)源码分析》:https://www.cnblogs.com/binarylei/p/10326046.html
一、Spring 中单例 bean 的管理
Spring 对单例 bean 的管理都是在 DefaultSingletonBeanRegistry 中完成的,这里会涉及到其内部所使用的几个内部属性:
// 1.1 保存最终创建成功的单例 beanName -> beanInstance
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 1.2 中间变量,beanName -> Objectfactory
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 1.3 中间变量,bean 还在创建的时候就可以获取,用于检测循环引用
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
这里涉及用于存储 bean 的不同的 Map,可能让人感到崩溃,简单解释如下:
singletonObjects
:用于保存 beanName 和创建 bean 实例之间的关系。beanName -> beanInstancesingletonFactories
:用于保存 beanName 和对象工厂的引用关系,一旦最终对象被创建(通过 objectFactory.getObject()),此引用信息将删除。beanName -> ObjectfactoryearlySingletonObjects
:用于保存 beanName 和创建的原始 bean 的引用关系,注意这里是原始 bean,即使用工厂方法或构造方法创建出来的对象,一旦对象最终创建好,此引用信息将删除。 与 singletonObjects 的不同之处在于,此时 bean 还在创建过程中,而且之后还可以进行增强,也就是代理后这两个 bean 就不是同一个了。可以通过 getBean 方法获取到了,其目的是用来检测循环引用。
从上面的解释,可以看出,这 singletonFactories 和 earlySingletonObjects 都是一个临时的辅助状态。在所有的对象创建完毕之后,此两个对象的 size 都为 0。那么再来看下这两个对象如何进行协作:
(1) 方法1:创建单例对象
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 1. 将这个 bean 添加到 singletonsCurrentlyInCreation 集合中,这样就可以判断 bean 是否存在创建
beforeSingletonCreation(beanName);
// 2. 初始化 bean,委托给 ObjectFactory 完成
singletonObject = singletonFactory.getObject();
// 3. 从 singletonsCurrentlyInCreation 移除该 bean
afterSingletonCreation(beanName);
// 4. 创建完成进行注册,这样下次就可以从缓存中直接获取这个对象了
addSingleton(beanName, singletonObject);
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
(2) 方法2:单例对象创建完成进行注册
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
(3) 方法3:将实例化后的对象暴露到容器中
Spring 在 bean 实例化后就会调用 addSingletonFactory 将这个对象提前暴露到容器中,这们就可以通过 getBean(A) 得到这个对象,即使这个对象仍正在创建。用于解决循环依赖。
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
(4) 方法4:从缓存中获取 bean
这个方法也是专门用于解决循环依赖的问题,当不存在循环依赖时 earlySingletonObjects 总是 null。
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) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
二、Spring 创建 bean 过程
我们从 BeanFactory#getBean(beanName) 调用说起,看一下这几个方法的调用顺序:
2.1 AbstractBeanFactory#doGetBean
这个方法先从缓存中获取 bean,没有再创建 bean,因此会调用方法 4 和方法 1,我们看一下调用过程。
(1) getSingleton(beanName, true)
doGetBean 首先从缓存中获取数据,Object sharedInstance = getSingleton(beanName)
,这个方法最终会调用 getSingleton(beanName, true)
。
(2) getSingleton(beanName, singletonFactory)
如果缓存中没有 bean,则会调用 addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)
来创建一个新 bean,代码如下:
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
一旦调用 getSingleton(beanName, singletonFactory) 方法,这个方法创建开始时就会标记这个 bean 为正在创建,创建结束后移除对应的标记。直接创建 bean 的过程实际上是委托给了 createBean 方法。继续跟踪这个方法。
2.2 AbstractAutowireCapableBeanFactory#doCreateBean
doCreateBean 方法中完成了单例的 bean 有以下几个主要的步骤:
createBeanInstance
实例化 bean 对象,一般是通过反射调用默认的构造器。populateBean
bean 属性注入,在这个步骤会从 Spring 容器中查找对应属性字段的值,解决循环依赖问题。initializeBean
调用的 bean 定义的初始化方法。
(3) addSingletonFactory(beanName, singletonFactory)
在 createBeanInstance 后 populateBean 前 Spring 会将这个实例化的 bean 提前暴露到容器中,这样 populateBean 属性注入时就可以通过 getBean(A) 查找到。
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
(4) getSingleton(beanName, false)
在 bean 初始化完成还后还需要进行依赖的检查,这时因为提前暴露的这个 bean(即使用工厂方法或构造方法创建出来的对象) initializeBean 还可以进行增强,这样这两个 bean 就不是同一个了。Spring 默认是不允许这种情况发生的。
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name \'" + beanName + "\' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"\'getBeanNamesOfType\' with the \'allowEagerInit\' flag turned off, for example.");
}
}
}
}
(5) addSingleton(beanName, singletonObject)
在 bean 创建结束后还有一步是在 getSingleton(beanName, singletonFactory) 中完成的,调用 addSingleton(beanName, singletonObject),即注册最终的 bean,同时清空中间的辅助状态。
这样单例 bean 的创建过程就完成了,下面就需要分析循环引用下 singletonFactories、earlySingletonObjects 这两个集合的状态。
三、循环引用下 Bean 状态分析
3.1 正常情况
在正常的情况下,调用顺序如下:以下有无,表示是否持有对指定 Bean 的引用
过程 | 方法 | singletonFactories | earlySingletonObjects | singletonObjects |
---|---|---|---|---|
缓存中获取 | getSingleton(beanName, true) | 无 | 无 | 无 |
创建 bean | getSingleton(beanName, singletonFactory) | 无 | 无 | 无 |
提前暴露到容器中 | addSingletonFactory(beanName, singletonFactory) | 有 | 无 | 无 |
依赖检查 | getSingleton(beanName, false) | 有 | 无 | 无 |
注册 | addSingleton(beanName, singletonObject) | 无 | 无 | 有 |
可以看到正常情况下,单例 bean 暴露的对象只会出现在 singletonFactories 集合中,不可能出现在 earlySingletonObjects 集合中,除非在创建 bean 的过程中又调用了 getSingleton(beanName, true) 方法,也就是此时出现了循环引用。
3.2 循环引用
但是出现循环引用之后呢,就会出现这种情况:
过程 | 方法 | singletonFactories | earlySingletonObjects | singletonObjects |
---|---|---|---|---|
缓存中获取 A | getSingleton(A, true) | A无B无 | A无B无 | A无B无 |
创建 A | getSingleton(A, singletonFactory) | A无B无 | A无B无 | A无B无 |
暴露 A 到容器中 | addSingletonFactory(A, singletonFactory) | A有B无 | A无B无 | A无B无 |
populateBean(A, mbd, instanceWrapper) A 注入 B 时又依赖了 A,此时由 B 准备解析 A…… | ||||
缓存中获取 A | getSingleton(A, true) | A无B有 | A有B无 | A无B无 |
注册 B | addSingleton(B, singletonObject) | A无B无 | A有B无 | A无B有 |
populateBean(A, mbd, instanceWrapper) 完成属性注入 | ||||
A- = initializeBean(beanName, exposedObject, mbd) 在 initializeBean 之后 A 变为 A- | ||||
依赖检查 | getSingleton(beanName, false) | A无B无 | A有B无 | A无B有 |
注册 A | getSingleton(beanName, false) | A无B无 | A无B无 | A有B有 |
在上面这个过程中,在对 A 进行验证时,就会从 earlySingletonObjects 中取得一个 A,但是这个 A 和后面的 A- 可能不是同一个对象,这是因为有了 beanPostProcessor 存在,它可以改变 bean 的最终值,比如对原始 bean 进行封装,代理等。在这个过程中,出现了 3 个对象 A, A-, B,而 B 中所持有的 A 对象为原始的 A。如果这里的 A 和 A- 不是同一个对象,即产生了 beanA 有了 beanB 的引用,但 beanB 并没有 beanA 的引用,而是另一个 beanA 的引用。
每天用心记录一点点。内容也许不重要,但习惯很重要!
以上是关于Spring 循环引用源码分析的主要内容,如果未能解决你的问题,请参考以下文章