Spring中的三級(jí)緩存與循環(huán)依賴詳解
一. 前言
Spring 的三級(jí)緩存、循環(huán)依賴,我們經(jīng)常聽(tīng)到這兩個(gè)詞,包括面試也會(huì)被面試官問(wèn)及三級(jí)緩存是啥?為啥需要三級(jí)緩存?循環(huán)依賴是啥?Spring 是如何解決循環(huán)依賴的?什么樣的循環(huán)依賴 Spring 無(wú)法解決?
帶著上述的問(wèn)題,我們深入看一下 Spring BeanFactory 的 getBean() 流程;這篇文章需要看官有一定的 Spring 源碼了解;
二. 三級(jí)緩存是指哪三個(gè)
三級(jí)緩存其實(shí)對(duì)應(yīng)了三個(gè) Map,它是在 DefaultSingletonBeanRegistry 類(lèi)里作為成員變量的;
public class DefaultSingletonBeanRegistry
extends SimpleAliasRegistry
implements SingletonBeanRegistry {
/** Cache of singleton factories: bean name to ObjectFactory. */
private Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
/** Cache of singleton objects: bean name to bean instance. */
private Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// ...
}- 三級(jí)緩存 singletonFactories:可以看到存的是 ObjectFactory 對(duì)象,第三級(jí)緩存可以根據(jù)對(duì)象是否需要?jiǎng)?chuàng)建代理而提前創(chuàng)建出代理對(duì)象;或者是創(chuàng)建出普通對(duì)象;
- 二級(jí)緩存 earlySingletonObjects:顧名思義,它存儲(chǔ)的是一個(gè)早期對(duì)象,存的是半成品對(duì)象或者半成品對(duì)象的代理對(duì)象,用來(lái)解決對(duì)象創(chuàng)建過(guò)程中的循環(huán)依賴問(wèn)題;(這里為什么說(shuō)是一個(gè)半成品對(duì)象,因?yàn)檫@里存儲(chǔ)的對(duì)象的屬性可能沒(méi)有注入完全);
- 一級(jí)緩存 singletonObjects:這里存的就是成品對(duì)象,實(shí)例化和初始化都完成了,我們項(xiàng)目中使用的對(duì)象都是在一級(jí)緩存中獲取的,一級(jí)緩存中存放代理對(duì)象,普通對(duì)象;
三. getBean()流程
我們通過(guò) BeanFactory 的 getBean() 看一下三級(jí)緩存的全流程;
直接進(jìn)入到 AbstractBeanFactory 的 getBean();
// --------------------------- AbstractBeanFactory ---------------------------
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
// --------------------------- AbstractBeanFactory ---------------------------
protected <T> T doGetBean(
String name, Class<T> requiredType, Object[] args, boolean typeCheckOnly) {
// ...
// 我們直接看主流程
// Create bean instance.
if (mbd.isSingleton()) {
// 這里調(diào)用 DefaultSingletonBeanRegistry#getSingleton()
// 參數(shù)一為 beanName
// 參數(shù)二為 ObjectFactory 函數(shù)式對(duì)象
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// ...
}在 AbstractBeanFactory#doGetBean() 中調(diào)用了 DefaultSingletonBeanRegistry#getSingleton(),并且這個(gè) getSingleton() 的參數(shù)二是一個(gè) ObjectFactory 函數(shù)式對(duì)象,這個(gè)函數(shù)式對(duì)象的實(shí)現(xiàn)邏輯是 return createBean();
我們先看 DefaultSingletonBeanRegistry#getSingleton();
// ------------------------ DefaultSingletonBeanRegistry -----------------------
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
// 1. 先從一級(jí)緩存中去獲取
// 如果獲取到了 bean 對(duì)象,直接返回
// 沒(méi)有獲取到 bean 對(duì)象的話,進(jìn)入后續(xù)創(chuàng)建 bean 對(duì)象流程
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException();
}
// ...
try {
// 2. 調(diào)用 ObjectFactory 對(duì)象的 getObject() 創(chuàng)建得到 bean 對(duì)象
// 從上述分析我們知道最終實(shí)現(xiàn)是 return createBean()
// 我們需要看 createBean() 流程
singletonObject = singletonFactory.getObject();
newSingleton = true;
} catch (IllegalStateException ex) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
} finally {
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 3. 創(chuàng)建 bean 成功的情況下
// 將 bean 對(duì)象放入到一級(jí)緩存 singletonObjects 中
// 并將 beanName 對(duì)應(yīng)的值從二級(jí)緩存、三級(jí)緩存中移除
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
// ------------------------ DefaultSingletonBeanRegistry -----------------------
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 將 bean 對(duì)象放入一級(jí)緩存中
// 并將 beanName 對(duì)應(yīng)的值從二級(jí)緩存、三級(jí)緩存中移除
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}核心實(shí)現(xiàn)是 ObjectFactory 的 createBean(),我們看 createBean() 邏輯;
// --------------------- AbstractAutowireCapableBeanFactory -------------------
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) {
RootBeanDefinition mbdToUse = mbd;
// ...
try {
// 調(diào)用 doCreateBean() 創(chuàng)建出 bean 對(duì)象,并返回該 bean 對(duì)象
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
return beanInstance;
} catch (Throwable ex) {
throw new BeanCreationException();
}
}
// --------------------- AbstractAutowireCapableBeanFactory -------------------
protected Object doCreateBean(String beanName, RootBeanDefinition mbd,
Object[] args) throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 1. 實(shí)例化對(duì)象
// 根據(jù)合適的構(gòu)造方法構(gòu)造出實(shí)例 bean 對(duì)象
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// 2. 是否應(yīng)該使用三級(jí)緩存,一般情況下都會(huì)使用三級(jí)緩存
boolean earlySingletonExposure = (mbd.isSingleton() &&
this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 3. 將 beanName 和對(duì)應(yīng)的函數(shù)式對(duì)象 ObjectFactory 放入到三級(jí)緩存中
// 該 ObjectFactory 已經(jīng)拿到了剛剛實(shí)例化好的 bean 對(duì)象,只不過(guò)只執(zhí)行了構(gòu)造函數(shù)
// 該 ObjectFactory 的 getObject() 實(shí)現(xiàn)是調(diào)用 getEarlyBeanReference()
addSingletonFactory(beanName,
() -> getEarlyBeanReference(beanName, mbd, bean));
}
// 4. 初始化 Bean 對(duì)象
Object exposedObject = bean;
try {
// 4.1 屬性注入
// 如果 A 依賴 B,getBean(A) 時(shí)會(huì)去調(diào) getBean(B)
// 如果 A、B 出現(xiàn)循環(huán)依賴,會(huì)出現(xiàn) getBean(A) -> getBean(B) -> getBean(A) 的情況
populateBean(beanName, mbd, instanceWrapper);
// 4.2 初始化 bean 對(duì)象
// 這里會(huì)執(zhí)行一些 BeanPostProcessor 的后處理方法
// 我們熟悉的 Spring AOP 就是在這里生成的代理類(lèi)對(duì)象的
// 如 @Transactional 使用的后處理器是 AbstractAutoProxyCreator
// 如 @Async 使用的后處理器是 AbstractAdvisingBeanPostProcessor
// 雖然都是生成 AOP 對(duì)象,但是這兩者在處理循環(huán)依賴時(shí)處理邏輯不一樣,后面細(xì)講
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
throw ex;
}
if (earlySingletonExposure) {
// 5. 決定 getBean(A) 是返回 bean 對(duì)象還是拋出異常
// 參數(shù)二是 false
// 從一級(jí)緩存或者二級(jí)緩存中獲取 bean 對(duì)象
// 走到這里一般是嘗試從二級(jí)緩存中獲取 bean 對(duì)象
// 這里比較繞,我們后面再講
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping &&
hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>();
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException();
}
}
}
}
// 6. 注冊(cè)刪除 bean 邏輯
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException();
}
// 7. 返回 bean 對(duì)象
return exposedObject;
}到此這篇關(guān)于Spring三級(jí)緩存與循環(huán)依賴的文章就介紹到這了,更多相關(guān)Spring三級(jí)緩存與循環(huán)依賴內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot定時(shí)任務(wù)兩種(Spring Schedule 與 Quartz 整合 )實(shí)現(xiàn)方法
本篇文章主要介紹了SpringBoot定時(shí)任務(wù)兩種(Spring Schedule 與 Quartz 整合 )實(shí)現(xiàn)方法,詳細(xì)的介紹了Spring Schedule 與 Quartz 整合的兩種方法,有興趣的可以了解一下。2017-03-03
Java數(shù)學(xué)工具類(lèi)MathUtil詳解
這篇文章主要為大家詳細(xì)介紹了Java數(shù)學(xué)工具類(lèi)MathUtil的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08
Eclipse項(xiàng)目有紅感嘆號(hào)的解決方法
這篇文章主要為大家詳細(xì)介紹了Eclipse項(xiàng)目有紅感嘆號(hào)的解決方法,給出了Eclipse項(xiàng)目有紅感嘆號(hào)的原因,以及如何解決?,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
SpringBoot2.動(dòng)態(tài)@Value的實(shí)現(xiàn)方式
這篇文章主要介紹了SpringBoot2.動(dòng)態(tài)@Value的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
Spring中的@CrossOrigin注冊(cè)處理方法源碼解析
這篇文章主要介紹了Spring中的@CrossOrigin注冊(cè)處理方法源碼解析,@CrossOrigin是基于@RequestMapping,@RequestMapping注釋方法掃描注冊(cè)的起點(diǎn)是equestMappingHandlerMapping.afterPropertiesSet(),需要的朋友可以參考下2023-12-12
java實(shí)現(xiàn)大文件導(dǎo)出的實(shí)現(xiàn)與優(yōu)化
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)大文件導(dǎo)出的實(shí)現(xiàn)與優(yōu)化的相關(guān)資料,文中的示例代碼講解詳細(xì),對(duì)我們深入了解java有一定的幫助,感興趣的小伙伴可以了解下2023-11-11
MybatisPlus實(shí)現(xiàn)數(shù)據(jù)攔截的使用示例
在MyBatis-Plus中,可以通過(guò)自定義攔截器來(lái)實(shí)現(xiàn)對(duì)SQL語(yǔ)句的攔截和修改,本文就來(lái)介紹一下如何使用,具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10
mybatisplus解除分頁(yè)限制的實(shí)現(xiàn)
這篇文章主要介紹了mybatisplus解除分頁(yè)限制的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12

