Spring三級(jí)緩存
是為了解決對(duì)象間的循環(huán)依賴(lài)問(wèn)題顾画。
A依賴(lài)B滚婉,B依賴(lài)A,這就是一個(gè)簡(jiǎn)單的循環(huán)依賴(lài)瓣戚。
我們來(lái)先看看三級(jí)緩存的源碼:
獲取Bean的源碼
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
//1級(jí)緩存 用于存放 已經(jīng)屬性賦值 初始化后的 單列BEAN
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
//2級(jí)緩存 用于存在已經(jīng)實(shí)例化镐牺,還未做代理屬性賦值操作的 單例
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
//3級(jí)緩存 存儲(chǔ)單例BEAN的工廠
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
//已經(jīng)注冊(cè)的單例池里的beanName
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
//正在創(chuàng)建中的beanName
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
//緩存查找bean 如果1級(jí)沒(méi)有炫掐,從2級(jí)獲取,也沒(méi)有,從3級(jí)創(chuàng)建放入2級(jí)
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName); //1級(jí)
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName); //2級(jí)
if (singletonObject == null && allowEarlyReference) {
//3級(jí)緩存 在doCreateBean中創(chuàng)建了bean的實(shí)例后,封裝ObjectFactory放入緩存的
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//創(chuàng)建未賦值的bean
singletonObject = singletonFactory.getObject();
//放入到二級(jí)緩存
this.earlySingletonObjects.put(beanName, singletonObject);
//從三級(jí)緩存刪除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
}
注意上面的getSingleton()方法
添加到1級(jí)緩存
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
//放入一級(jí)緩存
this.singletonObjects.put(beanName, singletonObject);
//從三級(jí)緩存刪除
this.singletonFactories.remove(beanName);
//從二級(jí)緩存刪除
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
添加到三級(jí)緩存
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
//一級(jí)緩存沒(méi)有睬涧,放入三級(jí)緩存
this.singletonFactories.put(beanName, singletonFactory);
//從二級(jí)緩存刪除募胃,確保二級(jí)緩存沒(méi)有該bean
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
創(chuàng)建Bean的源碼
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
//實(shí)例化對(duì)象
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null;
Class<?> beanType = instanceWrapper != null ? instanceWrapper.getWrappedClass() : null;
//判斷是否允許提前暴露對(duì)象旗唁,如果允許,則直接添加一個(gè) ObjectFactory 到三級(jí)緩存
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
//添加三級(jí)緩存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
//填充屬性
this.populateBean(beanName, mbd, instanceWrapper);
//執(zhí)行初始化方法痹束,并創(chuàng)建代理
exposedObject = initializeBean(beanName, exposedObject, mbd);
return exposedObject;
}
通過(guò)這段代碼检疫,我們可以知道Spring
在實(shí)例化對(duì)象的之后,就會(huì)為其創(chuàng)建一個(gè) Bean
工廠祷嘶,并將此工廠加入到三級(jí)緩存中屎媳。
因此,Spring 一開(kāi)始提前暴露的并不是實(shí)例化的 Bean论巍,而是將 Bean 包裝起來(lái)的 ObjectFactory烛谊。為什么要這么做呢?
這實(shí)際上涉及到 AOP
环壤,如果創(chuàng)建的 Bean
是有代理的,那么注入的就應(yīng)該是代理 Bean
钞诡,而不是原始的 Bean
郑现。但是 Spring
一開(kāi)始并不知道 Bean
是否會(huì)有循環(huán)依賴(lài),通常情況下(沒(méi)有循環(huán)依賴(lài)的情況下)荧降,Spring
都會(huì)在完成填充屬性接箫,并且執(zhí)行完初始化方法之后再為其創(chuàng)建代理。但是朵诫,如果出現(xiàn)了循環(huán)依賴(lài)的話辛友,Spring
就不得不為其提前創(chuàng)建代理對(duì)象,否則注入的就是一個(gè)原始對(duì)象剪返,而不是代理對(duì)象废累。因此,這里就涉及到應(yīng)該在哪里提前創(chuàng)建代理對(duì)象脱盲?
Spring
的做法就是在 ObjectFactory
中去提前創(chuàng)建代理對(duì)象邑滨。它會(huì)執(zhí)行 getObject()
方法來(lái)獲取到 Bean。實(shí)際上钱反,它真正執(zhí)行的方法如下:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
// 如果需要代理掖看,這里會(huì)返回代理對(duì)象;否則返回原始對(duì)象
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
因?yàn)樘崆斑M(jìn)行了代理面哥,避免對(duì)后面重復(fù)創(chuàng)建代理對(duì)象哎壳,會(huì)在 earlyProxyReferences
中記錄已被代理的對(duì)象。
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 記錄已被代理的對(duì)象
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
我們回到獲取bean的方法getSingleton()
//緩存查找bean 如果1級(jí)沒(méi)有尚卫,從2級(jí)獲取,也沒(méi)有,從3級(jí)創(chuàng)建放入2級(jí)
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName); //1級(jí)
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName); //2級(jí)
if (singletonObject == null && allowEarlyReference) {
//3級(jí)緩存 在doCreateBean中創(chuàng)建了bean的實(shí)例后归榕,封裝ObjectFactory放入緩存的
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//創(chuàng)建未賦值的bean
singletonObject = singletonFactory.getObject();
//放入到二級(jí)緩存
this.earlySingletonObjects.put(beanName, singletonObject);
//從三級(jí)緩存刪除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
這里有個(gè)比較重要的點(diǎn)。
提前暴露的對(duì)象吱涉,對(duì)象實(shí)例化蹲坷,但是沒(méi)有進(jìn)行屬性填充驶乾,還沒(méi)有初始化,是一個(gè)不完整的對(duì)象循签, 這個(gè)對(duì)象存放在二級(jí)緩存中级乐,這個(gè)對(duì)象在三級(jí)緩存中十分重要,是解決循環(huán)依賴(lài)一個(gè)非常巧妙的地方县匠。
讓我們來(lái)分析一下“A的某個(gè)field或者setter依賴(lài)了B的實(shí)例對(duì)象风科,同時(shí)B的某個(gè)field或者setter依賴(lài)了A的實(shí)例對(duì)象”這種循環(huán)依賴(lài)的情景。
- A
doCreateBean()
初始化乞旦,由于還未創(chuàng)建贼穆,從一級(jí)緩存查不到,此時(shí)只是一個(gè)半成品(提前暴露的對(duì)象)兰粉,放入三級(jí)緩存singletonFactories
; - A發(fā)現(xiàn)自己需要B對(duì)象故痊,但是三級(jí)緩存中未發(fā)現(xiàn)B,創(chuàng)建B的半成品玖姑,放入
singletonFactories
; - B發(fā)現(xiàn)自己需要A對(duì)象愕秫,從一級(jí)緩存
singletonObjects
和二級(jí)緩存earlySingletonObjects
中未發(fā)現(xiàn)A,但是在三級(jí)緩存singletonFactories
中發(fā)現(xiàn)A焰络,將A放入二級(jí)緩存earlySingletonObjects
戴甩,同時(shí)從三級(jí)緩存刪除; - 將A注入到對(duì)象B中闪彼;
- B完成屬性填充甜孤,執(zhí)行初始化方法,將自己放入第一級(jí)緩存中(此時(shí)B是一個(gè)完整的對(duì)象)畏腕;
- A得到對(duì)象B缴川,將B注入到A中;
- A完成屬性填充描馅,初始化二跋,并放入到一級(jí)緩存中。
在創(chuàng)建過(guò)程中流昏,都是從三級(jí)緩存(對(duì)象工程創(chuàng)建不完整對(duì)象)扎即,將提前暴露的對(duì)象放入到二級(jí)緩存,從二級(jí)緩存拿到后况凉,完成初始化谚鄙,放入一級(jí)緩存。