Spring中的三級(jí)緩存與循環(huán)依賴詳解
一. 前言
Spring 的三級(jí)緩存、循環(huán)依賴,我們經(jīng)常聽到這兩個(gè)詞,包括面試也會(huì)被面試官問及三級(jí)緩存是啥?為啥需要三級(jí)緩存?循環(huán)依賴是啥?Spring 是如何解決循環(huán)依賴的?什么樣的循環(huán)依賴 Spring 無法解決?
帶著上述的問題,我們深入看一下 Spring BeanFactory 的 getBean() 流程;這篇文章需要看官有一定的 Spring 源碼了解;
二. 三級(jí)緩存是指哪三個(gè)
三級(jí)緩存其實(shí)對(duì)應(yīng)了三個(gè) Map,它是在 DefaultSingletonBeanRegistry 類里作為成員變量的;
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ì)象,用來解決對(duì)象創(chuàng)建過程中的循環(huán)依賴問題;(這里為什么說是一個(gè)半成品對(duì)象,因?yàn)檫@里存儲(chǔ)的對(duì)象的屬性可能沒有注入完全);
- 一級(jí)緩存 singletonObjects:這里存的就是成品對(duì)象,實(shí)例化和初始化都完成了,我們項(xiàng)目中使用的對(duì)象都是在一級(jí)緩存中獲取的,一級(jí)緩存中存放代理對(duì)象,普通對(duì)象;
三. getBean()流程
我們通過 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ì)象,直接返回 // 沒有獲取到 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ì)象,只不過只執(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 就是在這里生成的代理類對(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)文章
解決feignclient調(diào)用服務(wù),傳遞的中文數(shù)據(jù)成???問題
這篇文章主要介紹了解決feignclient調(diào)用服務(wù),傳遞的中文數(shù)據(jù)成???問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Servlet+JavaBean+JSP打造Java Web注冊(cè)與登錄功能
比作MVC的話,控制器部分采用Servlet來實(shí)現(xiàn),模型部分采用JavaBean來實(shí)現(xiàn),而大部分的視圖采用Jsp頁面來實(shí)現(xiàn),接下來我們就來詳細(xì)看看如何用Servlet+JavaBean+JSP打造Java Web注冊(cè)與登錄功能2016-05-05Java實(shí)現(xiàn)用Mysql存取圖片操作實(shí)例
這篇文章主要介紹了Java實(shí)現(xiàn)用Mysql存取圖片操作實(shí)例,本文講解了使用BLOB類型保存和讀取圖片的代碼實(shí)例,需要的朋友可以參考下2015-06-06詳解spring cloud中使用Ribbon實(shí)現(xiàn)客戶端的軟負(fù)載均衡
這篇文章主要介紹了詳解spring cloud中使用Ribbon實(shí)現(xiàn)客戶端的軟負(fù)載均衡,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01參數(shù)校驗(yàn)Spring的@Valid注解用法解析
這篇文章主要介紹了參數(shù)校驗(yàn)Spring的@Valid注解用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08Java多線程并發(fā)synchronized?關(guān)鍵字
這篇文章主要介紹了Java多線程并發(fā)synchronized?關(guān)鍵字,Java?在虛擬機(jī)層面提供了?synchronized?關(guān)鍵字供開發(fā)者快速實(shí)現(xiàn)互斥同步的重量級(jí)鎖來保障線程安全。2022-06-06