以下舉例皆針對單例模式討論
圖解參考:https://www.processon.com/view/link/60e3b0ae0e3e74200e2478ce?1、Spring 如何創(chuàng)建 Bean?
對于單例 Bean 來說,在 Spring 容器整個生命周期內,有且只有一個對象。Spring 在創(chuàng)建 Bean 過程中,使用到了三級緩存,即 DefaultSingletonBeanRegistry.java 中定義的:
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
以 com.gyh.general 包下的 OneBean 為例,debug springboot 啟動過程,分析 spring 是如何創(chuàng)建 bean 的。
參考圖中spring 創(chuàng)建 bean的過程。其中最關鍵的幾步有:1.getSingleton(beanName, true)
依次從一二三級緩存中查找 bean 對象,如果緩存中存在對象,則直接返回 (early);2.createBeanInstance(beanName, mbd, args)
選一個合適的構造函數,new 實例對象 (instance),此時的 instance 中依賴的屬性還都是 null,屬于半成品;3.singletonFactories.put(beanName, oneSingletonFactory)
利用上一步的 instance,構建一個 singletonFactory,并將其放到三級緩存中;4.populateBean(beanName, mbd, instanceWrapper)
填充 bean:為該 bean 定義的屬性創(chuàng)建對象或賦值;5.initializeBean("one",oneInstance, mbd)
初始化 bean:對 bean 進行初始化或其他加工,如生成代理對象 (proxy);6.getSingleton(beanName, false)
依次在一二級緩存中查找,檢查是否有因循環(huán)依賴導致提前生成的對象,有的話與初始化后的對象是否一致;2、Spring 如何解決循環(huán)依賴?
以 com.gyh.circular.threeCache 包下的 OneBean 和 TwoBean 為例 ,兩個 Bean 相互依賴(即形成閉環(huán))。參考圖中spring 解決循環(huán)依賴的過程可知,spring 利用三級緩中的 objectFactory 生成并返回一個 early 對象,提前暴露這個 early 地址,供其他對象依賴注入使用,以此解決循環(huán)依賴問題。3、Spring 不能解決哪些循環(huán)依賴?
3.1 循環(huán)中使用了@Async注解
3.1.1 為什么循環(huán)中使用了@Async 會報錯?
以 com.gyh.circular.async 包下的 OneBean 和 TwoBean 為例,兩個 bean 相互依賴,且 oneBean 中的方法使用了@Async 注解,此時啟動 spring 失敗,報錯信息為:org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a.one': Bean with name 'a.one' has been injected into other beans [a.two] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.
并通過 debug 代碼,發(fā)現(xiàn)報錯位置在 AbstractAutowireCapableBeanFactory#doCreateBean 方法內,由于 earlySingletonReference != null 且 exposedObject != bean,導致報錯。![5a2ee708-813f-11ed-8abf-dac502259ad0.png](https://file1.elecfans.com//web2/M00/9E/5E/wKgZomToFoaAG83hAANm4GzZvlc111.png)
3.1.2 循環(huán)中使用了@Async 一定會報錯嗎?
依然以 com.gyh.circular.async 包下的 OneBean 和 TwoBean 為例,兩個 bean 相互依賴,使 TwoBean (非 OneBean) 中的方法使用了@Async 注解,此時啟動 spring 成功,并未報錯。debug 代碼可知:雖然 TwoBean 使用了 @Async 注解,但其 earlySingletonReference = null; 故不會引起報錯。![5a6698f6-813f-11ed-8abf-dac502259ad0.png](https://file1.elecfans.com//web2/M00/9E/5E/wKgZomToFoaALTC2AAPlZVy8gPs458.png)
3.1.3 為什么循環(huán)中使用了 @Transactional 不會報錯?
已知使用了 @Transactional 注解的 Bean,Spring 也會為其生成代理對象,但為什么這種 Bean 在循環(huán)里時不會產生報錯呢?以 com.gyh.circular.transactional 包下的 OneBean 和 TwoBean 為例,兩個 Bean 相互依賴,且 OneBean 中的方法使用了 @Transactional 注解,啟動 Spring 成功,并不會報錯。debug 代碼可知,生成 OneBean 過程中,雖然 earlySingletonReference != null,但 initializeBean 之后的 exposedObject 和 原始實例的地址相同(即 initializeBean 步驟中,并未對實例生成代理),所以不會產生報錯。![5ad3f7c0-813f-11ed-8abf-dac502259ad0.png](https://file1.elecfans.com//web2/M00/9E/5E/wKgZomToFoaAGUdgAAO7wLJSKnw152.png)
![5afa0d48-813f-11ed-8abf-dac502259ad0.png](https://file1.elecfans.com//web2/M00/9E/5E/wKgZomToFoaAJBrVAAPk-GbudT8054.png)
![5b3be77c-813f-11ed-8abf-dac502259ad0.png](https://file1.elecfans.com//web2/M00/9E/5E/wKgZomToFoeAa1_9AAN1hqp6fTg593.png)
![5b6c1d7a-813f-11ed-8abf-dac502259ad0.png](https://file1.elecfans.com//web2/M00/9E/5E/wKgZomToFoeAaP6yAAM97HMwCi0088.png)
3.1.5 為什么 @Async 在 getEarlyBeanReference 時不能返回一個 advice?
在 AbstractAutoProxyCreator#getAdvicesAndAdvisorsForBean 方法內,其主要做的事情有:1.找到當前 spring 容器中所有的 Advisor2.返回適配當前 bean 的所有 Advisor第一步返回的 Advisor 有 BeanFactoryCacheOperationSourceAdvisor 和 BeanFactoryTransactionAttributeSourceAdvisor,并無處理 Async 相關的 Advisor.刨根問底,追查為什么第一步不會返回處理 Async 相關的 Advisor?已知使用 @Async @Transactional @Cacheable 需要提前進行開啟,即提前標注 @EnableAsync、@EnableTransactionManagement、@EnableCaching 。以 @EnableTransactionManagement、@EnableCaching 為例,在其注解定義中,引入了 Selector 類,Selector 中又引入了 Configuration 類,在 Configuration 類中,創(chuàng)建了對應 Advisor 并放到了 spring 容器中,所以第一步才能得到這兩個 Advisor.而 @EnableAsync 的定義中引入的 Configuration 類,創(chuàng)建的是 AsyncAnnotationBeanPostProcessor 并非一個 Advisor,所以第一步不會得到它,所以 @Async 的 bean 不會在這一步被代理。3.2 構造函數引起的循環(huán)依賴
以 com.gyh.circular.constructor 包下的 OneBean 和 TwoBean 為例,兩個類的構造函數中各自依賴對方,啟動 spring,報錯:org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'c.one': Requested bean is currently in creation: Is there an unresolvable circular reference?
debug 代碼可知,兩個 bean 在根據構造函數 new instance 時,就已經陷入的死循環(huán),無法提前暴露可用的地址,所以只能報錯。4、如何解決以上循環(huán)依賴報錯?
1.不用 @Async,將需要異步操作的方法,放到線程池中執(zhí)行。(推薦)2.提出 @Async 標注的方法。(推薦)3.將使用 @Async 的方法提出到單獨的類中,該類只做異步處理,不做其他業(yè)務依賴,即避免形成循環(huán)依賴,從而解決報錯問題。參考 com.gyh.circular.async.extract 包。4.盡量不使用構造函數依賴對象。(推薦)5.破壞循環(huán)(不推薦)即不形成閉環(huán),在開發(fā)之前,規(guī)劃好對象依賴,方法調用鏈,盡量做到不使用循環(huán)依賴。(較難,隨著迭代開發(fā)不斷變化,很可能產生循環(huán))6.破壞創(chuàng)建順序(不推薦)7.由于使用 @Async 注解的所在類,比循環(huán)依賴內其他類先創(chuàng)建時才會報錯,那么想辦法使該類不先于其他類先創(chuàng)建,也可解決該問題,如:@DependsOn、@Lazy審核編輯 :李倩
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。
舉報投訴
-
源碼
+關注
關注
8文章
652瀏覽量
29452 -
spring
+關注
關注
0文章
340瀏覽量
14388
原文標題:從源碼層面深度剖析 Spring 循環(huán)依賴
文章出處:【微信號:OSC開源社區(qū),微信公眾號:OSC開源社區(qū)】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
深入剖析半導體濕法刻蝕過程中殘留物形成的機理
半導體濕法刻蝕過程中殘留物的形成,其背后的機制涵蓋了化學反應、表面交互作用以及側壁防護等多個層面,下面是對這些機制的深入剖析: 化學反應層面 1 刻蝕劑與半導體材料的交互:濕法刻蝕技術依賴
剖析 400V 變 660V 升壓變壓器助力光伏發(fā)電
利器。 從原理層面深度剖析,這款變壓器依托電磁感應原理運行。其核心部件鐵芯,選用高導磁率的優(yōu)質硅鋼片疊成,有效減少磁滯與渦流損耗,為高效的能量轉換筑牢根基。當 400V 交流電接入初級
![<b class='flag-5'>剖析</b> 400V 變 660V 升壓變壓器助力光伏發(fā)電](https://file1.elecfans.com/web3/M00/04/02/wKgZO2duD2CAEu_IAAr_En6QcOI216.png)
SSM框架的源碼解析與理解
的核心是控制反轉(IoC)和面向切面編程(AOP)。 源碼解析: Spring的源碼主要分為以下幾個部分: Bean容器: 負責實例化、配置和組裝對象。核心接口是 B
Spring事務實現(xiàn)原理
作者:京東零售 范錫軍 1、引言 spring的spring-tx模塊提供了對事務管理支持,使用spring事務可以讓我們從復雜的事務處理中得到解脫,無需要去處理獲得連接、關閉連接、事
![<b class='flag-5'>Spring</b>事務實現(xiàn)原理](https://file1.elecfans.com//web2/M00/0B/1B/wKgZomctcxKAXsmhAAEMko_SUCM423.png)
安全加速新選擇:深度剖析境外SOCKS5代理的優(yōu)勢與應用
在數字化時代,跨境網絡訪問已成為日常生活和工作中不可或缺的一部分。然而,網絡限制、延遲和安全問題常常阻礙著我們的順暢體驗。境外SOCKS5代理作為一種高效、安全的網絡解決方案,正逐漸成為許多用戶的新選擇。以下是對其優(yōu)勢與應用的深度剖析。
從設計到實施:樓宇自控系統(tǒng)建設流程的深度剖析
從設計到實施:樓宇自控系統(tǒng)建設流程的深度剖析 在探索現(xiàn)代建筑智能化的征途中,樓宇自控系統(tǒng)(BAS)無疑是引領變革的關鍵力量。它不僅深刻改變了建筑的管理模式,還極大地提升了建筑的運營效率與居住體驗。
Spring Cloud Gateway網關框架
Spring Cloud Gateway網關框架 本軟件微服務架構中采用Spring Cloud Gateway網關控制框架,Spring Cloud Gateway是Spring C
![<b class='flag-5'>Spring</b> Cloud Gateway網關框架](https://file1.elecfans.com/web2/M00/04/A6/wKgaombGkiKAAwa1AAE-bJwRAe8680.png)
Adafruit Huzzah無法從深度睡眠中醒來怎么辦?
我有一個問題,Huzzah 沒有從深度睡眠中醒來。
GPIO16 跳線到 Reset 引腳,GPIO0 和 GPIO2 都有 10k 上拉電阻到 V3.3。
如果我使用重置按鈕重置它,我會
發(fā)表于 07-19 15:04
玩轉Spring狀態(tài)機
說起Spring狀態(tài)機,大家很容易聯(lián)想到這個狀態(tài)機和設計模式中狀態(tài)模式的區(qū)別是啥呢?沒錯,Spring狀態(tài)機就是狀態(tài)模式的一種實現(xiàn),在介紹Spring狀態(tài)機之前,讓我們來看看設計模式中的狀態(tài)模式
![玩轉<b class='flag-5'>Spring</b>狀態(tài)機](https://file1.elecfans.com//web2/M00/F3/2C/wKgaomZ46Y6AHiVCAACxvB4H4AM171.png)
利用深度循環(huán)神經網絡對心電圖降噪
具體的軟硬件實現(xiàn)點擊 http://mcu-ai.com/ MCU-AI技術網頁_MCU-AI
我們提出了一種利用由長短期記憶 (LSTM) 單元構建的深度循環(huán)神經網絡來降 噪心電圖信號 (ECG
發(fā)表于 05-15 14:42
什么是RNN (循環(huán)神經網絡)?
循環(huán)神經網絡 (RNN) 是一種深度學習結構,它使用過去的信息來提高網絡處理當前和將來輸入的性能。RNN 的獨特之處在于該網絡包含隱藏狀態(tài)和循環(huán)。
發(fā)表于 02-29 14:56
?4200次閱讀
![什么是RNN (<b class='flag-5'>循環(huán)</b>神經網絡)?](https://file1.elecfans.com/web2/M00/C2/1B/wKgZomXgKxOACsTWAAAJbSjoWF0873.jpg)
評論