日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

淺談Spring 解決循環依賴必須要三級緩存嗎

瀏覽:190日期:2023-08-08 14:55:32

我們都知道 Spring 是通過三級緩存來解決循環依賴的,但是解決循環依賴真的需要使用到三級緩沖嗎?只使用兩級緩存是否可以呢?本篇文章就 Spring 是如何使用三級緩存解決循環依賴作為引子,驗證兩級緩存是否可以解決循環依賴。

循環依賴

既然要解決循環依賴,那么就要知道循環依賴是什么。如下圖所示:

淺談Spring 解決循環依賴必須要三級緩存嗎

通過上圖,我們可以看出:

A 依賴于 B B 依賴于 C C 依賴于 A

public class A { private B b;}public class B { private C c;}public class C { private A a;}

這種依賴關系形成了一種閉環,從而造成了循環依賴的局面。

下面是未解決循環依賴的常規步驟:

實例化 A,此時 A 還未完成屬性填充和初始化方法(@PostConstruct)的執行。 A 對象發現需要注入 B 對象,但是容器中并沒有 B 對象(如果對象創建完成并且屬性注入完成和執行完初始化方法就會放入容器中)。 實例化 B,此時 B 還未完成屬性填充和初始化方法(@PostConstruct)的執行。 B 對象發現需要注入 C 對象,但是容器中并沒有 C 對象。 實例化 C,此時 C 還未完成屬性填充和初始化方法(@PostConstruct)的執行。 C 對象發現需要注入 A 對象,但是容器中并沒有 A 對象。 重復步驟 1。

三級緩存

Spring 解決循環依賴的核心就是提前暴露對象,而提前暴露的對象就是放置于第二級緩存中。下表是三級緩存的說明:

名稱 描述 singletonObjects 一級緩存,存放完整的 Bean。 earlySingletonObjects 二級緩存,存放提前暴露的Bean,Bean 是不完整的,未完成屬性注入和執行 init 方法。 singletonFactories 三級緩存,存放的是 Bean 工廠,主要是生產 Bean,存放到二級緩存中。

所有被 Spring 管理的 Bean,最終都會存放在 singletonObjects 中,這里面存放的 Bean 是經歷了所有生命周期的(除了銷毀的生命周期),完整的,可以給用戶使用的。

earlySingletonObjects 存放的是已經被實例化,但是還沒有注入屬性和執行 init 方法的 Bean。

singletonFactories 存放的是生產 Bean 的工廠。

Bean 都已經實例化了,為什么還需要一個生產 Bean 的工廠呢?這里實際上是跟 AOP 有關,如果項目中不需要為 Bean 進行代理,那么這個 Bean 工廠就會直接返回一開始實例化的對象,如果需要使用 AOP 進行代理,那么這個工廠就會發揮重要的作用了,這也是本文需要重點關注的問題之一。

解決循環依賴

Spring 是如何通過上面介紹的三級緩存來解決循環依賴的呢?這里只用 A,B 形成的循環依賴來舉例:

實例化 A,此時 A 還未完成屬性填充和初始化方法(@PostConstruct)的執行,A 只是一個半成品。 為 A 創建一個 Bean 工廠,并放入到 singletonFactories 中。 發現 A 需要注入 B 對象,但是一級、二級、三級緩存均為發現對象 B。 實例化 B,此時 B 還未完成屬性填充和初始化方法(@PostConstruct)的執行,B 只是一個半成品。 為 B 創建一個 Bean 工廠,并放入到 singletonFactories 中。 發現 B 需要注入 A 對象,此時在一級、二級未發現對象 A,但是在三級緩存中發現了對象 A,從三級緩存中得到對象 A,并將對象 A 放入二級緩存中,同時刪除三級緩存中的對象 A。(注意,此時的 A 還是一個半成品,并沒有完成屬性填充和執行初始化方法) 將對象 A 注入到對象 B 中。 對象 B 完成屬性填充,執行初始化方法,并放入到一級緩存中,同時刪除二級緩存中的對象 B。(此時對象 B 已經是一個成品) 對象 A 得到對象 B,將對象 B 注入到對象 A 中。(對象 A 得到的是一個完整的對象 B) 對象 A 完成屬性填充,執行初始化方法,并放入到一級緩存中,同時刪除二級緩存中的對象 A。

我們從源碼中來分析整個過程:

創建 Bean 的方法在 AbstractAutowireCapableBeanFactory::doCreateBean()

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, Object[] args) throws BeanCreationException { BeanWrapper instanceWrapper = null; if (instanceWrapper == null) { // ① 實例化對象 instanceWrapper = this.createBeanInstance(beanName, mbd, args); } final Object bean = instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null; Class<?> beanType = instanceWrapper != null ? instanceWrapper.getWrappedClass() : null; // ② 判斷是否允許提前暴露對象,如果允許,則直接添加一個 ObjectFactory 到三級緩存 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { // 添加三級緩存的方法詳情在下方 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // ③ 填充屬性 this.populateBean(beanName, mbd, instanceWrapper); // ④ 執行初始化方法,并創建代理 exposedObject = initializeBean(beanName, exposedObject, mbd); return exposedObject;}

添加三級緩存的方法如下:

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, 'Singleton factory must not be null'); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { // 判斷一級緩存中不存在此對象 this.singletonFactories.put(beanName, singletonFactory); // 添加至三級緩存 this.earlySingletonObjects.remove(beanName); // 確保二級緩存沒有此對象 this.registeredSingletons.add(beanName); } }}@FunctionalInterfacepublic interface ObjectFactory<T> { T getObject() throws BeansException;}

通過這段代碼,我們可以知道 Spring 在實例化對象的之后,就會為其創建一個 Bean 工廠,并將此工廠加入到三級緩存中。

因此,Spring 一開始提前暴露的并不是實例化的 Bean,而是將 Bean 包裝起來的 ObjectFactory。為什么要這么做呢?

這實際上涉及到 AOP,如果創建的 Bean 是有代理的,那么注入的就應該是代理 Bean,而不是原始的 Bean。但是 Spring 一開始并不知道 Bean 是否會有循環依賴,通常情況下(沒有循環依賴的情況下),Spring 都會在完成填充屬性,并且執行完初始化方法之后再為其創建代理。但是,如果出現了循環依賴的話,Spring 就不得不為其提前創建代理對象,否則注入的就是一個原始對象,而不是代理對象。因此,這里就涉及到應該在哪里提前創建代理對象?

Spring 的做法就是在 ObjectFactory 中去提前創建代理對象。它會執行 getObject() 方法來獲取到 Bean。實際上,它真正執行的方法如下:

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;// 如果需要代理,這里會返回代理對象;否則返回原始對象exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } } return exposedObject;}

因為提前進行了代理,避免對后面重復創建代理對象,會在 earlyProxyReferences 中記錄已被代理的對象。

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware { @Override public Object getEarlyBeanReference(Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); // 記錄已被代理的對象 this.earlyProxyReferences.put(cacheKey, bean); return wrapIfNecessary(bean, beanName, cacheKey); }}

通過上面的解析,我們可以知道 Spring 需要三級緩存的目的是為了在沒有循環依賴的情況下,延遲代理對象的創建,使 Bean 的創建符合 Spring 的設計原則。

如何獲取依賴

我們目前已經知道了 Spring 的三級依賴的作用,但是 Spring 在注入屬性的時候是如何去獲取依賴的呢?

他是通過一個 getSingleton() 方法去獲取所需要的 Bean 的。

protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 一級緩存 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // 二級緩存 singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) {// 三級緩存ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) { // Bean 工廠中獲取 Bean singletonObject = singletonFactory.getObject(); // 放入到二級緩存中 this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName);} } } } return singletonObject;}

當 Spring 為某個 Bean 填充屬性的時候,它首先會尋找需要注入對象的名稱,然后依次執行 getSingleton() 方法得到所需注入的對象,而獲取對象的過程就是先從一級緩存中獲取,一級緩存中沒有就從二級緩存中獲取,二級緩存中沒有就從三級緩存中獲取,如果三級緩存中也沒有,那么就會去執行 doCreateBean() 方法創建這個 Bean。

二級緩存

我們現在已經知道,第三級緩存的目的是為了延遲代理對象的創建,因為如果沒有依賴循環的話,那么就不需要為其提前創建代理,可以將它延遲到初始化完成之后再創建。

既然目的只是延遲的話,那么我們是不是可以不延遲創建,而是在實例化完成之后,就為其創建代理對象,這樣我們就不需要第三級緩存了。因此,我們可以將addSingletonFactory() 方法進行改造。

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, 'Singleton factory must not be null'); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { // 判斷一級緩存中不存在此對象 object o = singletonFactory.getObject(); // 直接從工廠中獲取 Bean this.earlySingletonObjects.put(beanName, o); // 添加至二級緩存中 this.registeredSingletons.add(beanName); } }}

這樣的話,每次實例化完 Bean 之后就直接去創建代理對象,并添加到二級緩存中。測試結果是完全正常的,Spring 的初始化時間應該也是不會有太大的影響,因為如果 Bean 本身不需要代理的話,是直接返回原始 Bean 的,并不需要走復雜的創建代理 Bean 的流程。

結論

測試證明,二級緩存也是可以解決循環依賴的。為什么 Spring 不選擇二級緩存,而要額外多添加一層緩存呢?

如果 Spring 選擇二級緩存來解決循環依賴的話,那么就意味著所有 Bean 都需要在實例化完成之后就立馬為其創建代理,而 Spring 的設計原則是在 Bean 初始化完成之后才為其創建代理。所以,Spring 選擇了三級緩存。但是因為循環依賴的出現,導致了 Spring 不得不提前去創建代理,因為如果不提前創建代理對象,那么注入的就是原始對象,這樣就會產生錯誤。

到此這篇關于淺談Spring 解決循環依賴必須要三級緩存嗎的文章就介紹到這了,更多相關Spring 循環依賴三級緩存內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Spring
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
麻豆精品久久| 免费在线观看成人| 九九九精品视频| 国产欧美欧美| 综合亚洲自拍| 热三久草你在线| 精品国产麻豆| 久久国产影院| 蜜桃视频在线观看一区二区| 日韩二区三区在线观看| 国语精品一区| 少妇精品在线| 欧美久久精品一级c片| 国产精品久av福利在线观看| 蜜臀久久99精品久久久画质超高清| 国产精品日本| 男女男精品网站| 热三久草你在线| 亚洲精品国产精品粉嫩| 中文字幕在线视频久| 日韩午夜视频在线| 久久久久久一区二区| 日本欧美一区二区| 久久青草久久| 免费av一区| 正在播放日韩精品| 日本午夜精品一区二区三区电影| 亚洲高清av| 国产一区二区三区不卡av| 欧美亚洲精品在线| 国产精品xxx| 亚洲制服一区| 91久久视频| 1024精品一区二区三区| 成人影视亚洲图片在线| 欧美日一区二区在线观看| 亚洲一区区二区| 中文字幕系列一区| 福利一区和二区| 久久午夜精品一区二区| 亚洲视频电影在线| 国产精品不卡| 免费在线观看一区二区三区| 色乱码一区二区三区网站| 大香伊人久久精品一区二区| 精品日韩视频| 国产精品久久久久久妇女| 超碰成人av| 一区二区三区午夜视频| 日韩欧美一区二区三区免费观看| 粉嫩av一区二区三区四区五区 | 亚洲小说春色综合另类电影| 欧美激情亚洲| 亚洲欧美日韩高清在线| 久久99久久久精品欧美| 免费在线观看不卡| 红杏一区二区三区| 日韩av不卡一区二区| 亚洲专区一区| 在线国产一区二区| 91精品婷婷色在线观看| 久久三级毛片| 国产精品极品在线观看| 一区在线免费观看| 欧美一区久久久| 国产视频一区二| 蜜桃一区二区三区在线| 亚洲播播91| 91亚洲国产高清| 国产成人精品一区二区三区免费| 亚洲毛片网站| 久久中文字幕av| 日韩国产网站| 久久精品国产亚洲夜色av网站| 国产精品99免费看| 日韩免费久久| 欧美aa在线观看| 国产一区二区中文| 亚洲二区免费| 欧美日韩国产一区精品一区| 国产美女精品| 婷婷综合国产| 亚洲欧美网站在线观看| 欧美激情另类| 欧美日韩在线网站| 视频一区国产视频| 嫩草伊人久久精品少妇av杨幂 | 韩日一区二区三区| 蜜臀av性久久久久蜜臀aⅴ流畅| 亚洲女同中文字幕| 亚洲资源在线| 国产精品久久乐| 都市激情国产精品| sm捆绑调教国产免费网站在线观看| 欧美日韩一区二区国产| 日韩国产一二三区| 久久毛片亚洲| 欧美一区=区| 黄在线观看免费网站ktv| 91精品成人| 国产精品va| 午夜国产一区二区| 欧美交a欧美精品喷水| 91九色精品国产一区二区| 国产欧美丝祙| 色在线中文字幕| 青青草视频一区| 天堂网在线观看国产精品| 中文字幕视频精品一区二区三区| 麻豆91精品| 麻豆精品在线观看| 亚洲一区二区三区中文字幕在线观看| 黄色aa久久| 综合亚洲自拍| 国产99精品| 风间由美中文字幕在线看视频国产欧美 | 久久精品一区二区三区中文字幕| 国产亚洲一区在线| 日本а中文在线天堂| 欧美一级久久| 久久成人国产| 好吊日精品视频| 91亚洲国产| 国产精品欧美大片| 日韩成人精品一区二区三区| 亚洲作爱视频| 欧美在线影院| 久久99久久人婷婷精品综合| 国产色噜噜噜91在线精品| 中文字幕乱码亚洲无线精品一区| 亚洲一级高清| 欧美激情另类| 日韩精品dvd| 中文在线资源| 久久久久99| 国产农村妇女精品一二区| 欧美成人高清| 激情综合自拍| 丝袜美腿亚洲一区| 亚洲精品极品| 亚洲欧美一级| 国产日产一区| 国产一区2区| 三级小说欧洲区亚洲区| 国产精品女主播一区二区三区| 精品1区2区3区4区| 在线 亚洲欧美在线综合一区| 久久国产精品久久久久久电车| 亚洲欧洲日韩精品在线| 日韩精品一区二区三区中文字幕| 日韩一区二区三区在线看| 国产精品自拍区| 麻豆一区二区三| 免费在线亚洲| 久久久久久黄| 日韩午夜在线| 国产高清亚洲| 麻豆成人在线观看| 欧美日中文字幕| 蜜臀av一区二区在线免费观看| 欧美日韩亚洲一区三区| 国产一区欧美| 国产九九精品| 午夜久久美女| 鲁大师精品99久久久| 日韩午夜av| 四虎精品一区二区免费| 国产精品蜜月aⅴ在线| 亚洲综合电影| 日韩高清一级| 99视频精品全国免费| 国产伦精品一区二区三区在线播放| 成人午夜国产| 国产精品原创| 日韩区一区二| 亚洲国产福利| 国产图片一区| 亚洲精品极品少妇16p| 国产一区二区久久久久| 黑丝一区二区三区| 日韩欧美一区免费| 欧美日韩在线精品一区二区三区激情综合 | 免费视频一区二区三区在线观看| 激情五月综合网| 国产66精品| 亚洲男人在线| 蜜臀久久99精品久久久久宅男| 日本精品在线中文字幕| 精品美女在线视频| 在线综合亚洲| 国产一区二区三区四区大秀| 日韩精品五月天| 丝袜亚洲精品中文字幕一区| 夜久久久久久| 婷婷激情一区| 成人精品天堂一区二区三区| 欧美天堂一区| 成人av二区| 狠狠色狠狠色综合日日tαg| 欧美福利专区|