我们模拟A,B对象的循环依赖。
Bean的创建过程在
AbstractApplicationContext#refresh() -> finishBeanFactoryInitialization -> beanFactory.preInstantiateSingletons()的时候开始:
一次执行getBean() -> doGetBean() -> createBean() -> doCreateBean()
在doGetBean()方法中,我们可以看到:调用了
this.singletonObjects.get(beanName)
会优先从一级缓存中查找。
在A对象还未创建的时候,在一级缓存是拿不到Object的,所以doGetBean()方法回继续往下执行,一直到下图:
createBean()方法里传了一个lambda表达式,我们进去看看:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
...
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
...
}
里面调用了getObject()方法,
使用了@FunctionalInterface,说明是一个函数式接口,表明我们可以通过匿名内部类或者lambda表达式来调用getObject()方法来执行。
所以看似调用了 singletonFactory.getObject();方法,其实最终调用的是
createBean(beanName, mbd, args)方法,然后调用doCreateBean(),此时A对象已经是个半成品(只实例化,未初始化),里面有个
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
,看此方法:
先从一级缓存拿,如果拿不到,便将半成品A放到三级缓存singletonFactories中,k是beanName,v是传进去的lambda表达式
最终获取到A对象的半成品(只实例化,未初始化)。
继续开始填充属性
applyPropertyValues(beanName, mbd, bw, pvs);
里面当填充b属性的时候,发现此时的b属性类型是RuntimeBeanReferenc,进而执行处理逻辑,开始调用 this.beanFactory.getBean(refName); 由此又回到了对B对象的创建。
然后就是拿到了B对象的半成品,放入三级缓存。
接下来就是给B对象的a属性赋值,这里过程不详细讲解了,直接看一个重要的方法:
此时一二级缓存都为空,从三级缓存中拿,根据k,我们拿到了A对象所对应的lambda表达式
() -> getEarlyBeanReference(beanName, mbd, bean);所以当调用getObject()方法是,实际上调用的是getEarlyBeanReference方法。 然后就得到了A的半成品对象,然后把半成品对象放入二级缓存,k:A , v:A的半成品对象;然后清空三级缓存中A对象 ,此时B对象就实例化结束了,然后把B对象放入一级缓存,k:b, v: B对象,清空三级缓存中B对象。
然后给A对象的b属性赋值,就可以从一级缓存中拿到B对象,赋值给b属性,A对象就完整了。最后把A对象放入一级缓存,清空二三级缓存中的A对象。
到此,A,B对象创建成功,循环依赖结束。
问:只用一级缓存可不可以?
答:不可以,一级缓存放成品,二级缓存放半成品,成品和半成品要分开,全用一级缓存会导致逻辑没法执行。
问:只用一二级缓存可不可以?
答:第三级缓存作用的本质是AOP代理问题,如果可以保证不调用getEarlyBeanReference方法,就可以不使用三级缓存。即,如果对象之间没有循环依赖,也不存在代理,就可以不使用三级缓存。
问:为什么三级缓存可以解决AOP代理问题?
答:一个对象如果要被代理,在整个创建过程中必然包括此对象和代理对象,两者都是单例的且对象名相同,所以就要把两个对象分来存放,在使用的时候需要判断是否需要代理,如果需要则从三级缓存中拿代理对象。
ps:
三个Map分别对应我们说的一级缓存,二级缓存,三级缓存。
总结:流程图如下: