IOC的bean创建是支持循环依赖的(需配置allow-circular-references=true),看一下源码是如何处理的:
关键类是DefaultSingletonBeanRegistry,里边有3个map:
singletonObjects一级缓存,放已经创建好的bean
earlySingletonObjects二级缓存,放已经构造但是还未填充属性的bean
singletonFactories 三级缓存,放的是它的Factory,即创建createBean的lamda
比如我们有两个bean,DeviceManager和VeeService互相依赖,看一下他们的创建过程
先走到DeviceManager的getBean,
AbstractBeanFactory的
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
调用getSingleton,传入一个lamda,实际就是createBean
getSingleton的代码:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);//先从一级缓存获取
if (singletonObject == null) {
beforeSingletonCreation(beanName);
boolean newSingleton = false;
try {
singletonObject = singletonFactory.getObject(); //调用传入的lamda,其实就是createBean
newSingleton = true;
}
if (newSingleton) { // 将创建好的放到一级缓存
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
createBean关键部分:
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);//调用构造函数
}
//如果是单例并且支持循环依赖,并且bean在创建中
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");
}
//添加到三级缓存,这里添加的是一个lamda
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
////填充属性,会递归的去创建所依赖的bean。比如会创建veeService,
由于vee又依赖deviceManager,所以会调用getSingleton的时候,从三级缓存里找到deviceManager并执行lamda创建了提前暴露对象,放到二级缓存
populateBean(beanName, mbd, instanceWrapper);
//初始化bean,会创建AOP代理
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
if (earlySingletonExposure) {
//这里从二级缓存里拿出来了
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
}
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
return exposedObject;
这里createBean就完成了,出来就会把对象放到一级缓存,表示这个bean创建好了
然后getSingleton返回给其他bean去用了
上面就是从deviceManager的视角走完了bean的创建
下来再从veeService的视角看一下有啥不同:
一样的getSingleton调createBean
调构造函数后,放入三级缓存,然后来到populateBean填充属性,遇到deviceManager
调用createBean来创建VeeService,过程和上面的一样,同样会往三级缓存放lamda
然后走到填充属性,需要填充DeviceManager
调用doGetBean,调用DefaultSingletonBeanRegistry的getSingleton
此时DeviceManager还在三级缓存里,所以会从三级缓存拿到,调用lamda创建,放入二级缓存。这个过程还会给DeviceManager创建AOP代理(因为它用了springcache)
然后initializeBean,过程中会检查是否要AOP,结果是不需要
createBean结束后,回到getSingleton,是新建的bean,所以添加到一级缓存
可以看到,vee没有用到二级缓存
如果有三个bean循环依赖,A--》B---》C----》A
创建A,createBeanInstance调用A的构造函数,addSingletonFactory放到三级缓存
填充属性populateBean,getBean找B:
构造B,放三级缓存,填充属性,getBean找C:
构造C,放三级缓存,填充属性:
getBean获取A,getSingleton:
从三级缓存获取lamda执行,A放到二级缓存。过程中会wrapIfNecessary创建AOP
继续创建C,initializeBean,wrapIfNecessary 创建AOP
创建C完成,添加到一级缓存
继续B:initializeBean,wrapIfNecessary 添加到一级缓存
继续A:initializeBean,添加到一级缓存
可以看到,三个bean都有放到三级和一级缓存,但只有A放到了二级缓存
问题:如果关闭循环依赖,二级三级缓存还有没有用?
我认为是没用的。从源码看,添加三级缓存只有addSingletonFactory,它的调用点只有一个,条件是:
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
必须打开循环依赖开关,否则不会加三级缓存
二级缓存添加在getSingleton,它是从三级缓存获取lamda执行后获得对象放二级缓存,可见二级缓存是依赖三级缓存的