spring解決循環(huán)依賴
概述
循環(huán)依賴就是依賴關(guān)系形成環(huán),比如最簡(jiǎn)單的循環(huán)依賴:A對(duì)象依賴B,B對(duì)象依賴A
屬性注入與循環(huán)依賴
- 如果是構(gòu)造器注入,如果循環(huán)依賴對(duì)象沒(méi)法構(gòu)建,因?yàn)檫€未實(shí)例化
- 如果是屬性注入但是作用域是prototype,spring不會(huì)緩存其對(duì)象實(shí)例,也不能處理循環(huán)依賴的情況
- 如果是屬性注入singleton的,其bean的實(shí)例化過(guò)程與屬性注入過(guò)程是分開(kāi)的,并且spring提供了三個(gè)map(就是大家說(shuō)三級(jí)緩存)來(lái)實(shí)現(xiàn)。
spring屬性注入處理循環(huán)依賴的方式
通過(guò)以下xml方式配置一個(gè)循環(huán)依賴的示例:
<bean id="person1" class="com.example.leetcode.spring.bean.Person"> <property name="parent" ref="person2"></property> <property name="name" value="tom"></property> </bean> <bean id="person2" class="com.example.leetcode.spring.bean.Person"> <property name="parent" ref="person1"></property> <property name="name" value="jack"></property> </bean>
spring循環(huán)依賴處理幾個(gè)關(guān)鍵位置:
獲取bean對(duì)象
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean; // 這里會(huì)檢查單例bean是否已經(jīng)在注冊(cè)表,并返回。 // Eagerly check singleton cache for manually registered singletons. Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { if (logger.isTraceEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); } } bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } ... }
DefaultSingletonBeanRegistry(單例對(duì)象注冊(cè)表)的幾個(gè)關(guān)鍵屬性。
// 用來(lái)存儲(chǔ)已經(jīng)創(chuàng)建好的單例對(duì)象 /** Cache of singleton objects: bean name to bean instance. */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 用來(lái)存儲(chǔ)單例beanname到ObjectFactory的映射 /** Cache of singleton factories: bean name to ObjectFactory. */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 用來(lái)提前存儲(chǔ)還未初始化好的單例對(duì)象 /** Cache of early singleton objects: bean name to bean instance. */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
DefaultSingletonBeanRegistry.getSingleton()的實(shí)現(xiàn).
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) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
AbstractAutowireCapableBeanFactory.doCreateBean創(chuàng)建對(duì)象與注入屬性
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { ... instanceWrapper = createBeanInstance(beanName, mbd, args); ... // 檢查是否提前將單例bean存入緩存 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } // 這里將beanname與工廠映射放入緩存注冊(cè)表中(也就是上面的singletonFactories) addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } ... // 注入依賴屬性 populateBean(beanName, mbd, instanceWrapper); ... }
假設(shè)我們從beanfactory獲取person1對(duì)象, 循環(huán)依賴處理流程如下:
1.通過(guò)AbstractBeanFactory.doGetBean("persion1")獲取對(duì)象
2.因?yàn)橐婚_(kāi)始通過(guò)DefaultSingletonBeanRegistry.getSingleton()什么都沒(méi)有,進(jìn)入AbstractAutowireCapableBeanFactory.doCreateBean()進(jìn)行創(chuàng)建
3.AutowireCapableBeanFactory.doCreateBean()里面執(zhí)行完創(chuàng)建邏輯,因?yàn)槭莝ingleton將beanname與工廠的映射加入到addSingletonFactory()到緩存
4.開(kāi)始處理person1對(duì)象的屬性依賴populateBean()
5.當(dāng)發(fā)現(xiàn)person1的parent屬性是一個(gè)引用時(shí),通過(guò)beanfactory.getBean("person2")獲取依賴對(duì)象(org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveReference)
6.此時(shí)進(jìn)入person2的創(chuàng)建流程, person2也沒(méi)有緩存,開(kāi)始實(shí)例化并加入到addSingletonFactory()到緩存
7.person2在通過(guò)populateBean()注入屬性依賴發(fā)現(xiàn)依賴person1, 此時(shí)通過(guò)beanfactory.getBean("person1")獲取依賴對(duì)象
8.此時(shí)AbstractBeanFactory.doGetBean("persion1")獲取對(duì)象執(zhí)行到getSingleton("person1")進(jìn)行以下判斷:
- 從singletonObjects.get(beanName)獲取到null
- 進(jìn)入if條件,對(duì)singletonObjects同步
- 從earlySingletonObjects.get(beanName);獲取也為null
- 進(jìn)入內(nèi)層if,通過(guò)singletonFactories.get(beanName);獲取到最開(kāi)始bean實(shí)例化之后的beanname與工廠緩存信息
- 獲取到僅實(shí)例化完成的bean,并earlySingletonObjects.put(beanName, singletonObject);
- 然后刪除singletonFactories.remove(beanName);
9.此時(shí)從getSingleton("person1")返回了一個(gè)僅實(shí)例化尚未注入的bean引用
10.person2在第7步獲取到person1僅實(shí)例化未注入的對(duì)象引用。
11.person2完成屬性注入并返回。
12.person2被addSingleton(beanName, singletonObject);中singletonObjects.put(beanName, singletonObject)緩存,并刪除singletonFactories.remove(beanName);earlySingletonObjects.remove(beanName);
13.person1在5步獲取到person2的對(duì)象并完成屬性注入
14.person1對(duì)象返回(因?yàn)橐婚_(kāi)始person2獲取的是person1的引用,此時(shí)person1完成注入是能看到注入后的對(duì)象)
15.person1被addSingleton(beanName, singletonObject);中singletonObjects.put(beanName, singletonObject)緩存,并刪除singletonFactories.remove(beanName);earlySingletonObjects.remove(beanName);
16.返回最終的person1對(duì)象
關(guān)于三個(gè)map(三級(jí)緩存)
在出現(xiàn)循環(huán)依賴時(shí),三個(gè)map之間的流程如下:
先從singletonFactories獲取工廠,并通過(guò)getObject獲取對(duì)象并移除緩存,將對(duì)象緩存到earlySingletonObjects
通過(guò)earlySingletonObjects獲取提前曝光的對(duì)象
對(duì)象創(chuàng)建并初始化完成之后,對(duì)象信息保留在singletonObjects并移除過(guò)earlySingletonObjects中的緩存
earlySingletonObjects二級(jí)緩存是雞肋嗎?
earlySingletonObjects緩存的目的是,通過(guò)三級(jí)緩存在獲取對(duì)象會(huì)執(zhí)行一些列的后置處理器,通過(guò)earlySingletonObjects來(lái)緩存提升性能。
以上就是spring解決循環(huán)依賴的詳細(xì)內(nèi)容,更多關(guān)于sping 循環(huán)依賴的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Spring為何要用三級(jí)緩存來(lái)解決循環(huán)依賴問(wèn)題
- 淺談Spring 解決循環(huán)依賴必須要三級(jí)緩存嗎
- spring解決循環(huán)依賴的簡(jiǎn)單方法
- Spring IOC原理補(bǔ)充說(shuō)明(循環(huán)依賴、Bean作用域等)
- Spring如何解決循環(huán)依賴的問(wèn)題
- 簡(jiǎn)單了解Spring beanfactory循環(huán)依賴命名重復(fù)屬性
- Spring循環(huán)依賴的解決辦法,你真的懂了嗎
- 基于SpringBoot構(gòu)造器注入循環(huán)依賴及解決方式
- 淺談Spring如何解決循環(huán)依賴的問(wèn)題
- 詳解Spring循環(huán)依賴的解決方案
- spring 如何解決循環(huán)依賴
相關(guān)文章
利用spring boot如何快速啟動(dòng)一個(gè)web項(xiàng)目詳解
這篇文章主要給大家介紹了關(guān)于利用spring boot如何快速啟動(dòng)一個(gè)web項(xiàng)目的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧、2017-12-12java的Jackson將json字符串轉(zhuǎn)換成泛型List
這篇文章主要介紹了java的Jackson將json字符串轉(zhuǎn)換成泛型List ,這里整理了詳細(xì)的代碼,有需要的小伙伴可以參考下。2017-02-02Java實(shí)現(xiàn)帶GUI的氣泡詩(shī)詞效果
這篇文章主要為大家介紹了如何利用Java實(shí)現(xiàn)帶GUI的氣泡詩(shī)詞效果,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Java有一定幫助,感興趣的可以了解一下2022-12-12說(shuō)說(shuō)@ModelAttribute在父類和子類中的執(zhí)行順序
這篇文章主要介紹了@ModelAttribute在父類和子類中的執(zhí)行順序,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06idea手動(dòng)執(zhí)行maven命令的三種實(shí)現(xiàn)方式
這篇文章主要介紹了idea手動(dòng)執(zhí)行maven命令的三種實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08