關(guān)于Spring源碼是如何解決Bean的循環(huán)依賴
首先需要明白一點(diǎn),只有scop為(singleton)單例類型的Bean,spring才支持循環(huán)依賴。
scope為(prototype)原型類型的Bean是不支持的,它每次調(diào)用都會(huì)創(chuàng)建一個(gè)新的實(shí)例,spring 在實(shí)例化bean的時(shí)候會(huì)先實(shí)例化bean的各種屬性依賴,如果TestA TestB是原型類型且相互依賴則會(huì)出現(xiàn)new TestA 的時(shí)候,先new TestB,然后new TestB的時(shí)候又去new TestA會(huì)出現(xiàn)無限套娃的情況。
兩個(gè)單例testA testB 互相依賴的實(shí)例化過程
Spring容器創(chuàng)建單例“testA”bean
首先根據(jù)無參構(gòu)造器創(chuàng)建bean,并暴露一個(gè)“ObjectFactory”用于返回一個(gè)提前暴露正在創(chuàng)建中的bean,并將“testA”標(biāo)識(shí)符放到“當(dāng)前創(chuàng)建bean池”,然后進(jìn)行setter注入“testB”。
Spring容器創(chuàng)建單例“testB”bean
首先根據(jù)無參構(gòu)造器創(chuàng)建bean,并暴露一個(gè)“ObjectFactory”用于返回一個(gè)提前暴露正在創(chuàng)建中的bean,并將“testB”標(biāo)識(shí)符放到“當(dāng)前創(chuàng)建bean池”,然后進(jìn)行setter注入“testA”,此時(shí)由于通過 暴露"ObjectFactory" 已提前暴露了一個(gè)正在創(chuàng)建中的"testA" bean,所以直接注入,完成testB的創(chuàng)建,注入testA中,再完成testA的創(chuàng)建。
源碼中的實(shí)現(xiàn)方式
首先了解一下創(chuàng)建Bean過程中最重要的三個(gè)map
以下三個(gè)Map均來自于 DefaultSingletonBeanRegistry
Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
singletonObjects
:用于保存BeanName和創(chuàng)建bean實(shí)例之間的關(guān)系,bean name 一> bean instance。singletonFactories
:用于保存BeanName和創(chuàng)建bean的工廠之間的關(guān)系,bean name 一>ObjectFactory。earlySingletonObjects
:也是保存BeanName和創(chuàng)建bean實(shí)例之間的關(guān)系,與singletonObjects的不同之處在于,當(dāng)一個(gè)單例bean被放到這里面后,那么當(dāng)bean還在創(chuàng)建過程中,就可以通過getBean方法獲取到了,其目的是用來檢測(cè)循環(huán)引用。
總結(jié):后面兩個(gè)Map實(shí)際上就是為了輔助第一個(gè)Map緩存Bean的實(shí)例,完成后數(shù)據(jù)就在后面兩個(gè)Map中清掉了。
測(cè)試代碼:
// 1. 引入依賴,springboot項(xiàng)目只需要這一個(gè)依賴即可測(cè)試 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> // 2. 兩個(gè)測(cè)試類 @Component public class TestA { @Autowired private TestB testB; } @Component public class TestB { @Autowired private TestA testA; }
注意:下面所有的方法都只是源碼的部分截取,把我認(rèn)為重要的邏輯放在這里的,大家閱讀時(shí),可提前在IDE中打開文中提到的幾個(gè)類,在相應(yīng)方法處,打上斷點(diǎn)可以直接調(diào)試,bean的實(shí)例化過程就一目了然了。
1. AbstractBeanFactory類中g(shù)etBean方法
public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args) throws BeansException { return doGetBean(name, requiredType, args, false); }
2. AbstractBeanFactory類中doGetBean方法
// // 2.1 從緩存中獲取實(shí)例Bean,第一次肯定沒有,為null Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null); }else{ // Create bean instance. if (mbd.isSingleton()) { // 2.2 獲取緩存中的實(shí)例 sharedInstance = getSingleton(beanName, () -> { try { // 2.3 調(diào)用創(chuàng)建Bean實(shí)例的方法 return createBean(beanName, mbd, args); } }); beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } }
3. DefaultSingletonBeanRegistry類中g(shù)etSingleton方法
2.1調(diào)用的就是這里的3.1
// 3.1 @Override @Nullable public Object getSingleton(String beanName) { return getSingleton(beanName, true); } // 3.2 @Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { // Quick check for existing instance without full singleton lock Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { synchronized (this.singletonObjects) { // Consistent creation of early reference within full singleton lock singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } } } return singletonObject; }
3.1和3.2后面會(huì)反復(fù)獲取的,第一次因?yàn)閕sSingletonCurrentlyInCreation(beanName)返回false,所以返回null
4. DefaultSingletonBeanRegistry類中g(shù)etSingleton方法
獲取ObjectFactory,2.2就是調(diào)用的這里
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { beforeSingletonCreation(beanName); boolean newSingleton = false; try { // 4.0.1 singletonObject = singletonFactory.getObject(); newSingleton = true; } finally { afterSingletonCreation(beanName); } if (newSingleton) { // 4.0.2 addSingleton(beanName, singletonObject); } } return singletonObject; } } protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { this.singletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }
這面重點(diǎn)分析beforeSingletonCreation 、afterSingletonCreation 和 addSingleton這三個(gè)方法
4.1 DefaultSingletonBeanRegistry 中 beforeSingletonCreation方法 和 afterSingletonCreation方法
protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } } protected void afterSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) { throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation"); } }
重點(diǎn):這兩個(gè)方法的目的就是為 singletonsCurrentlyInCreation 這個(gè)set集合添加和刪除當(dāng)前創(chuàng)建的Bean,為后續(xù)處理做鋪墊,addSingleton方法主要是對(duì)Bean緩存map的維護(hù)。
4.2 現(xiàn)在回到第4步的4.0.1的方法中,singletonObject = singletonFactory.getObject();這行代碼就是正真獲取實(shí)例對(duì)象的地方,singletonFactory 是怎么拿到的呢,這就要回到第2步的2.3步驟中,通過createBean方法返回了ObjectFactory類型的singletonFactory,下面看createBean是如何創(chuàng)建Bean的:
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { try { Object beanInstance = doCreateBean(beanName, mbdToUse, args); return beanInstance; } } // 看過一些spring源碼的都應(yīng)該明白spring真正做事情的都是以doXXX開頭的,這里也不例外 // 相信大家都已經(jīng)明白真正創(chuàng)建Bean是由doCreateBean方法實(shí)現(xiàn)的,下面我們繼續(xù)分析這個(gè)方法 protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { BeanWrapper instanceWrapper = null; if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } Object bean = instanceWrapper.getWrappedInstance(); boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } Object exposedObject = bean; try { // 4.3 填充Bean的屬性,依賴bean就是這里初始化的 populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd); } if (earlySingletonExposure) { // 4.4 再次獲取緩存中的實(shí)例,注意這里可以從兩個(gè)緩存處獲取,第一個(gè)是earlySingletonObjects map,第二個(gè)是singletonFactories map獲取 Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } } } return exposedObject; } // isSingletonCurrentlyInCreation方法 public boolean isSingletonCurrentlyInCreation(String beanName) { return this.singletonsCurrentlyInCreation.contains(beanName); } // 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)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }
重要流程梳理
1.doCreateBean中主要分為兩部分
第一部分通過instanceWrapper得到BeanFactory的實(shí)例,內(nèi)部由反射實(shí)現(xiàn),這里我們不多做分析,變量earlySingletonExposure,它由三部分得到,前面兩個(gè)都很容易理解,第三部分則出現(xiàn)了我們?cè)?.1中做鋪墊的集合 singletonsCurrentlyInCreation。
由于在4.1中已經(jīng)設(shè)置了,所以earlySingletonExposure肯定為true,因此執(zhí)行addSingletonFacatory為singletonFactories map賦值,完成了beanName -> ObjectFactory的映射
2.populateBean方法中
則會(huì)完成對(duì)Bean依賴屬性的注入,因此代碼走到4.3的時(shí)候,testA的創(chuàng)建就停止了,會(huì)回到第一步去獲取testB,然后又是對(duì)testB的創(chuàng)建,最后會(huì)再次走到4.3,完成testA 和 testB 的ObjectFactory的映射,即將它們放入 singletonFactories map緩存中。
3.創(chuàng)建testB 再次走到4.3的時(shí)候
又會(huì)去初始化testB的依賴 testA,此時(shí)會(huì)再次去第一步獲取,再次走到2.1的時(shí)候,因?yàn)閠estA的ObjectFactory是有值的,所以通過它能夠獲取到testA 的singletonObject,此時(shí)就把testA 的實(shí)例放入了 earlySingletonObjects中,只不過此時(shí)的testA實(shí)例是不完整的,還沒有完成屬性testB依賴的初始化。
最后返回testA的singletonObject引用,完成testB對(duì)其依賴testA的初始化,然后再去 4.4 獲取testB的緩存,這里依舊是沒有的,然后返回到4.0.2處,將testB加入singletonObjects map緩存,并移除testB在singletonFactories中的緩存,這里testB 在 earlySingletonObjects中實(shí)際上是沒有值的,當(dāng)然有的話也會(huì)移除,因?yàn)閟ingletonObjects 中已經(jīng)拿到值了,所以另外兩個(gè)輔助map就不用保留數(shù)據(jù)了。
4.上面已經(jīng)完成testB的初始化
并放入singletonObjects,緩存了,繼續(xù)走,就又回到了4.3,繼續(xù)完成對(duì)testA的創(chuàng)建,走到4.4的時(shí)候,繼續(xù)去緩存中獲取testA,因?yàn)橹耙呀?jīng)把testA放入earlySingletonObjects map中了,所以4.4是直接能夠獲取到testA的實(shí)例的。
5.繼續(xù)走,就又來到了4.0.2
不過這次是針對(duì)testA的,addSingleton方法中會(huì)把testA的實(shí)例給放入singletonObjects map緩存中,同時(shí)移除singletonFactories 和 earlySingletonObjects map緩存的testA,完成testA和testB的實(shí)例化。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring?Data?JPA?注解Entity關(guān)聯(lián)關(guān)系使用詳解
這篇文章主要為大家介紹了Spring?Data?JPA?注解Entity關(guān)聯(lián)關(guān)系使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09spring boot actuator監(jiān)控超詳細(xì)教程
Spring Boot Actuator就是一款可以幫助你監(jiān)控系統(tǒng)數(shù)據(jù)的框架,其可以監(jiān)控很多很多的系統(tǒng)數(shù)據(jù),接下來通過本文給大家介紹spring boot actuator監(jiān)控超詳細(xì)教程,感興趣的朋友一起看看吧2021-10-10Java 對(duì) Cookie增刪改查的實(shí)現(xiàn)示例
這篇文章主要介紹了Java 對(duì) Cookie增刪改查的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05Java實(shí)現(xiàn)將PDF轉(zhuǎn)為PDF/A
通過將PDF格式轉(zhuǎn)換為PDF/A格式,可保護(hù)文檔布局、格式、字體、大小等不受更改,從而實(shí)現(xiàn)文檔安全保護(hù)的目的,同時(shí)又能保證文檔可讀、可訪問。本文將為大家介紹如何實(shí)現(xiàn)這一轉(zhuǎn)換,需要的可以參考一下2022-01-01一次java異步任務(wù)的實(shí)戰(zhàn)記錄
最近做項(xiàng)目的時(shí)候遇到了一個(gè)小問題,從前臺(tái)提交到服務(wù)端A,A調(diào)用服務(wù)端B處理超時(shí),下面這篇文章主要給大家介紹了一次java異步任務(wù)的實(shí)戰(zhàn)記錄,需要的朋友可以參考下2022-05-05springboot注入yml配置文件 list報(bào)錯(cuò)的解決方案
這篇文章主要介紹了springboot注入yml配置文件 list報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08Java中Integer的parseInt和valueOf的區(qū)別詳解
這篇文章主要介紹了Java中Integer的parseInt和valueOf的區(qū)別詳解,nteger.parseInt(s)是把字符串解析成int基本類型,Integer.valueOf(s)是把字符串解析成Integer對(duì)象類型,其實(shí)int就是Integer解包裝,Integer就是int的包裝,需要的朋友可以參考下2023-11-11