前言
在讲解本片文章开始之前,必须要先学会上一篇文章创建对象的整体流程,否则这篇文章会学习的很吃力的。
什么是循环引用?
这个很好理解,多个bean之间相互依赖,形成了一个闭环。
比如:A依赖于B、B依赖于A。
public class A{
B b;
}
public class B{
A a;
}
引出循环引用
创建对象的两个步骤:
- 获取
- 创建
引出关于循环引用的疑问
在创建对象流程的基础上,分析 A依赖于B、B依赖于A,这种情况创建对象的流程。
- 获取A对象:getBean("A");
- 到单例池中获取A对象:getSingleton("A"),此时单例池中肯定是没有A对象的,所以需要创建A对象。
- 创建A对象:doCreateBean("A")。
- 实例化A对象:createBeanInstance("A"),此时的A并不是一个完整的A,此时属性并没有被填充,接下来需要对A进行属性填充。
- 填充A对象的属性:populateBean("A"),而填充属性的过程中,因为A依赖B,所以在这一步就会去创建B对象
- 获取B对象:getBean("B");
- 到单例池中获取B对象:getSingleton("B"),此时单例池中肯定是没有B对象的,所以需要创建B对象。
- 创建B对象:doCreateBean("B")。
- 实例化B对象:createBeanInstance("B"),此时的B并不是一个完整的B,此时属性并没有被填充,接下来需要对B进行属性填充。
- 填充B对象的属性:populateBean("B"),而填充属性的过程中,因为B依赖A,因为前面的操作,A并没有被创建完,所以此时也肯定获取不到A对象,那么又该去创建A了,这时候就出现了套娃的情况。
- 获取B对象:getBean("B");
这时候,就出现了如同套娃一样的循环引用的问题,那么接下来就开始讲解怎么解决这种问题吧。
解决循环引用的前提:
两个对象都是单实例的情况下,且通过set方式进行注入才能成功。
三个缓存对象的地方
单例池:singletonObjects
//这个map是缓存所有创建好的对象的地方,也就是经过了整个创建周期的对象(实例化、属性填充、初始化)
//DefaultSingletonBeanRegistry类里面的属性。
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
早期单例池:earlySingletonObjects
//这个map是缓存还没有创建好的对象,并且是需要提前暴漏出来的对象。
//DefaultSingletonBeanRegistry 类里面的属性。
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
单例工厂池:singletonFactories
//这个map是缓存还没有创建好的对象,这里存储的是
//DefaultSingletonBeanRegistry 类里面的属性。
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
循环引用流程分析
第一步:尝试获取对象A
方法坐标:
getBean() #doGetBean()#getSingleton(String beanName, boolean allowEarlyReference)
方法源码:
//此时参数 allowEarlyReference=true
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//从单例池中获取:没有
Object singletonObject = this.singletonObjects.get(beanName);
//判断A是否正在创建:没有正在创建,直接返回null
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
第二步:标记A正在创建
方法坐标
getBean() #doGetBean()#getSingleton(String beanName, ObjectFactory<?> singletonFactory) #beforeSingletonCreation()
方法源码:
protected void beforeSingletonCreation(String beanName) {
//this.singletonsCurrentlyInCreation.add(beanName) 将A放到正在创建的set中
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
第三步:创建对象A的实例
方法坐标:
getBean() #doGetBean()#getSingleton() #createBean()#doCreateBean()
截取方法部分源码:
//先执行实例化对象的方法
instanceWrapper = createBeanInstance(beanName, mbd, args);
//this.allowCircularReferences=true
//isSingletonCurrentlyInCreation =true 这个为true就是在上一步设置的。
//所以这个整体为 true
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//将A放到singletonFactories(单例工厂池)中,key是A的id,value是创建A对象的一个lambda表达式,
//如果A对象没有做代理,那么这个lambda表达式返回的就是A对象本身
//如果A对象做了代理,那么这个lambda表达式返回的就是A的代理对象
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
第四步:A对象进行属性填充,填充B对象
方法坐标:
getBean() #doGetBean()#getSingleton() #createBean()#doCreateBean()#populateBean()#applyPropertyValues()
截取方法部分源码:
//applyPropertyValues()这个方法里面有如下这一行代码:
//这行代码主要工作是做转换用的:
//如果是TypedStringValue类型:将TypeStringValue转换成 "1"
//如果是 RuntimeBeanReference类型:从工厂中获得B对象,
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
//上面那行代码的resolveValueIfNecessary()方法里面有如下代码:
//因为B对象是RuntimeBeanReference类型的,所以执行if块中的resolveReference()方法
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
return resolveReference(argName, ref);
}
//上面那行代码的resolveReference()方法里面有如下代码:
//resolvedName就是B对象的id,
//当发现B依赖了A,所以会通过这行代码进行getBean("B")的操作,然后又会执行接下来的步骤:
//1.尝试获取B对象
//2.标记B正在创建。
//3.创建B对象的实例
//4.B对象进行属性填充,填充B属性的时候,会发现B又依赖了A,所以会通过这行代码再次进行getBean("A")的操作,注意,再次获取A时发生了变化,咱们往下接着看
this.beanFactory.getBean(resolvedName);
第五步:再次获取对象A(在填充对象B属性时)
方法坐标:
getBean() #doGetBean()#getSingleton(String beanName, boolean allowEarlyReference)
方法源码:
//此时参数 allowEarlyReference=true
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//从单例池中获取:没有
Object singletonObject = this.singletonObjects.get(beanName);
//判断A是否正在创建:A正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//从早期单例池获取A对象:没有
singletonObject = this.earlySingletonObjects.get(beanName);
//if块为true
if (singletonObject == null && allowEarlyReference) {
//锁住单例池,防止并发,做个安全保险
synchronized (this.singletonObjects) {
//再次从单例池中获取A对象,做个安全保险:获取不到
singletonObject = this.singletonObjects.get(beanName);
//if块为true
if (singletonObject == null) {
//再次从早期单例池获取A对象:没有
singletonObject = this.earlySingletonObjects.get(beanName);
//if块为true
if (singletonObject == null) {
//从单例工厂池获取A对象:能获取到
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
//if块为true
if (singletonFactory != null) {
//调用单例工厂池存储的lambda表达式,获取A对象
//注意:A对象如果做了代理,此处获取的就是A的代理对象,如果A没有做代理,那么此处获取的就是A对象本身。
singletonObject = singletonFactory.getObject();
//将A放入早期单例池,将这个不完整的对象提前暴漏出来,因为对象的引用一般都是对象的地址值,所以提前将对象暴漏出来,后期再赋值就可以了。
this.earlySingletonObjects.put(beanName, singletonObject);
//将A从单例工厂池移除
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
第六步:实例化B对象
在第五步能获取到提前暴漏出来的A对象,直接返回给B,此时B正处在属性填充阶段
方法坐标:
getBean() #doGetBean()#getSingleton() #createBean()#doCreateBean()#initializeBean()
截取方法部分源码:
exposedObject = initializeBean(beanName, exposedObject, mbd);
第七步:更新B对象存储的位置
方法坐标:
getBean() #doGetBean()#getSingleton(lambda)
- 截取方法部分源码:
finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } afterSingletonCreation(beanName); } //上面finally块afterSingletonCreation()方法内部实现如下: //判断B是否是被排除的Bean,同时将B从正在创建列表(singletonsCurrentlyInCreation)中移除。 protected void afterSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) { throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation"); } }
- 截取方法部分源码:
if (newSingleton) { addSingleton(beanName, singletonObject); } //上面if块addSingleton()方法内部实现如下: protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { //将B对象放入单例池中 this.singletonObjects.put(beanName, singletonObject); //将B对象从单例工厂池中移除 this.singletonFactories.remove(beanName); //将B对象从早期单例池中移除 this.earlySingletonObjects.remove(beanName); //将B对象添加到已经注册过的set集合中。 this.registeredSingletons.add(beanName); } }
第八步:实例化A对象,更新A的存储位置
具体代码参考实例化B对象,更新B的存储位置
总结
这篇文章细品,那么在此给大家留个问题:
单例工厂池(singletonFactories)存储的是创建对象的lambda表达式,那么不考虑循环引用的问题,如果只创建一个对象A,并且A对象不会涉及循环引用,A对象是在哪里调用的singletonFactories的getObject()方法获取的A对象。