參考文章:Spring 為何需要三級緩存解決循環(huán)依賴宪塔,而不是二級緩存
Spring是如何利用"三級緩存"巧妙解決Bean的循環(huán)依賴問題
個(gè)人理解:
1、其實(shí)把getEarlyBeanReference生成的對象直接保存到二級緩存囊拜,無需三級緩存用ObjectFacotry封裝原始bean也可以解決循環(huán)依賴某筐。三級緩存感覺純粹是為了延遲調(diào)用aop邏輯而已。
2冠跷、其實(shí)把getEarlyBeanReference生成的對象直接暴露到一級緩存也是可以的南誊。只要引用的地址不變身诺,誰要用就提前給誰。初始化動(dòng)作可以后面慢慢做抄囚。只要引用不變霉赡,它初始化完成后,所有引用它的bean都自然而然的能得到完成的該bean幔托⊙鳎可能spring擔(dān)心一級緩存既用來存放單例bean,又用來存放提前暴露的bean重挑,會(huì)引起混亂嗓化。所以,上面徐庶老師說的谬哀,只要膽子大刺覆,一級緩存夠用。解決循環(huán)依賴的核心史煎,不在乎幾級緩存谦屑,而在于提前暴露引用地址即可。
一篇梭、先交代下什么是循環(huán)依賴氢橙,什么是三級緩存
循環(huán)依賴:A依賴B,B依賴A
三級緩存:
//一級緩存很洋,用來存放初始化完成的單例bean
Map<String, Object> singletonObjects;
//二級緩存充蓝,用來存放提前暴露的原始bean
Map<String, Object> earlySingletonObjects;
//三級緩存,用來存放 “包裝提前暴露的原始bean”的ObjectFactory對象
Map<String, ObjectFactory<?>> singletonFactories;
有人可能會(huì)問喉磁,提前暴露的對象已經(jīng)存放在二級緩存了谓苟,為啥還要在三級緩存中存放呢?下文會(huì)詳細(xì)解釋协怒。
二涝焙、循環(huán)依賴的解決
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
.....//省略
getBean的時(shí)候,一上來就先去拿一下提前暴露的bean對象孕暇。
getSingleton方法如下:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//先去一級緩存拿仑撞。新創(chuàng)建的bean,這里一定拿不到
Object singletonObject = this.singletonObjects.get(beanName);
//拿不到初始化完成的bean妖滔,且該bean正在被創(chuàng)建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//優(yōu)先去二級緩存拿隧哮,如果沒有再去三級緩存拿。有了座舍,就直接返回沮翔。
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
//最后一步,去三級緩存拿
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//調(diào)用三級緩存ObjectFactory的getObject得到提前暴露的對象曲秉。
singletonObject = singletonFactory.getObject();
//放到二級緩存中采蚀,然后刪除三級緩存疲牵。可見:同一個(gè)提前暴露的bean榆鼠,只能要么在三級緩存纲爸,要么在二級緩存。
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
我們肯定會(huì)有疑問妆够,不就去拿一個(gè)尚未初始化完成的bean對象而已嘛识啦?有一個(gè)地方存一下,這里取出來责静,不就行了嘛袁滥。為啥非要在搞一個(gè)三級緩存呢?
想知道三級緩存做了啥灾螃,就要看下三級緩存的ObjectFactory.getObject到底做了啥题翻?
三、三級緩存的ObjectFactory.getObject到底做了啥腰鬼?
提前暴露對象的代碼在doCreateBean里面嵌赠。如下:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//實(shí)例化bean,尚未初始化
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
mbd.resolvedTargetType = beanType;
..... //省略
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
//判斷是否可以提前暴露熄赡。判斷條件 = 是否單例 && 是否允許(默認(rèn)true) && 是否創(chuàng)建過程中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//這里把尚未初始化的bean姜挺,包裝成ObjectFactory對象傳遞給addSingletonFactory方法
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
//注入屬性
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
//調(diào)用初始化方法,初始化bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
...//省略
我們看下解決循環(huán)依賴的核心方法addSingletonFactory彼硫,如下:
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
//bean已經(jīng)被其他線程初始化完成放到一級緩存了炊豪,這里也沒必要放到三級緩存
if (!this.singletonObjects.containsKey(beanName)) {
//放到三級緩存,然后刪除二級緩存(以防有值)
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
可以看到拧篮,這里僅僅是把提前暴露的bean封裝成的ObjectFactory词渤,放到三級緩存中。
是不是還是不明白為啥需要三級緩存串绩?我們看下上面添加的匿名內(nèi)部類ObjectFactory的實(shí)現(xiàn)缺虐。
new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
}
答案就在getEarlyBeanReference方法里面,如下:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
//對于有SmartInstantiationAwareBeanPostProcessor礁凡,特殊處理
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
if (exposedObject == null) {
return null;
}
}
}
}
return exposedObject;
}
這個(gè)地方專門用來特殊處理SmartInstantiationAwareBeanPostProcessor接口高氮,說明getEarlyBeanReference也是一個(gè)拓展點(diǎn),作用在這里的生命周期顷牌。getEarlyBeanReference的實(shí)現(xiàn)到底是啥呢剪芍?我們找個(gè)最常見的實(shí)現(xiàn)類,AbstractAutoProxyCreator窟蓝∽锕看下它的方法:
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
this.earlyProxyReferences.add(cacheKey);
}
return wrapIfNecessary(bean, beanName, cacheKey);
}
很神奇的地方,我們在看下AbstractAutoProxyCreator.postProcessAfterInitialization方法:
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
getEarlyBeanReference和postProcessAfterInitialization,何其相似胺凰!;口芍!
postProcessAfterInitialization我們知道,它只會(huì)在bean的所有初始化方法完成后雇卷,調(diào)用aop生成代理類鬓椭。但是getEarlyBeanReference竟然對尚未初始化完成的bean,提前進(jìn)行了aop代理关划。Why小染?不用初始化完成,就能代理嗎贮折?
一直想不通裤翩,最后終于想通了。既然尚未初始化完成的bean都可以提前注入到其他bean里面调榄,為啥就不能提前AOP呢踊赠?我們用的是bean的引用,只要這個(gè)引用不變每庆,至于引用所指向的對象啥時(shí)候初始化完筐带,其實(shí)無所謂。其他bean也只是持有的是這個(gè)bean的引用缤灵,同理AOP代理也是僅僅持有target bean的引用伦籍。所以,所有使用到bean的地方腮出,只要實(shí)例化完成生成了引用地址帖鸦,只要這個(gè)地址不變,就可以把這個(gè)bean當(dāng)做成熟的bean使用利诺。等整個(gè)容器啟動(dòng)完成富蓄,這些bean自然而然的就初始化好了,所有引用這個(gè)bean的Bean也自然而然的就可以使用了慢逾。
到這里大家應(yīng)該清楚了立倍,為啥需要三級緩存了吧。如果你依賴的對象是AOP代理侣滩,那么就需要用到第三級緩存暫存ObjectFactory口注。
你可能又會(huì)問,為啥這里不直接把getEarlyBeanReference生成的對象君珠,放到二級緩存里面呢寝志?這樣不也節(jié)省了三級緩存嘛?為啥非要在getBean.getSingleton里面去調(diào)用getObject呢?
這個(gè)問題問得好,其實(shí)我也覺得可以材部。我目前也沒答案毫缆,可能spring基于效率的考慮吧。
個(gè)人理解:
1乐导、其實(shí)把getEarlyBeanReference生成的對象直接保存到二級緩存苦丁,無需三級緩存用ObjectFacotry封裝原始bean也可以解決循環(huán)依賴。三級緩存感覺純粹是為了延遲調(diào)用aop邏輯而已物臂。
2旺拉、其實(shí)把getEarlyBeanReference生成的對象直接暴露到一級緩存也是可以的。只要引用的地址不變棵磷,誰要用就提前給誰蛾狗。初始化動(dòng)作可以后面慢慢做。只要引用不變仪媒,它初始化完成后沉桌,所有引用它的bean都自然而然的能得到完成的該bean∷惴裕可能spring擔(dān)心一級緩存既用來存放單例bean蒲牧,又用來存放提前暴露的bean,會(huì)引起混亂赌莺。所以冰抢,上面徐庶老師說的,只要膽子大艘狭,一級緩存夠用挎扰。解決循環(huán)依賴的核心,不在乎幾級緩存巢音,而在于提前暴露引用地址即可遵倦。