阿里面試:Spring 循環依賴的 “奪命連環問”

大家好,歡迎來到 Tlog4J 課堂,我是 Jensen。

面試官:Spring 是如何解決循環依賴問題的?

候選人:Spring 用了三級緩存來解決這個問題

面試官:能具體講講它的工作原理嗎?

候選人:啊這……

Spring 循環依賴其實是 Spring 當中非常典型的一個問題,也非常難的一道題,因爲回答這個問題本身會特別繞,而且這不僅僅是一道題這麼簡單,它後面會引發面試官一系列的奪命連環問。

那今天咱一起把循環依賴涉及的問題認真梳理一遍。

0x1

Spring 循環依賴奪命連環問

一問:什麼是循環依賴?

循環依賴指的是多個對象之間的依賴關係形成一個閉環。

我們都知道,如果在代碼中,把兩個或者多個 Bean 相互之間去持有對方的引用,就會發生循環依賴,循環依賴會導致注入出現死循環,這是 Spring 發生循環依賴的一個原因。

二問:Spring 的循環依賴有哪幾種形態?

第一種是互相依賴,也就是 A 依賴 B,B 又依賴 A,它們之間形成了一個循環依賴:

第二種是三者之間的依賴,也就是 A 依賴 B,B 依賴 C,C 又依賴 A,形成了一個循環依賴:

第三種是自我依賴,也就是說 A 與 A 之間形成的循環依賴。

現實中由於依賴層次深、關係複雜等因素, 導致循環依賴可能並不是那麼一目瞭然。

三問:Spring 設計三級緩存分別存放什麼內容?

三級緩存說白了就是三個 Map 容器,在框架裏頭這種 Map 容器是隨處可見:

/** Cache of singleton objects: bean name to bean instance. */

private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of early singleton objects: bean name to bean instance. */

private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

/** Cache of singleton factories: bean name to ObjectFactory. */

private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

四問:Spring 這個三級緩存具體的工作原理是什麼?

循環依賴主要是解決這個 “死循環” 沒有 “出口” 的問題,只要我們找到這個“出口”,循環依賴就能迎刃而解。

我們分四個步驟來說:

  1. 當我們通過 getBean(A.class) 去獲得 A 對象實例的時候,Spring 會先去一級緩存裏邊找,如果發現一級緩存沒有找到 BeanA,就去創建這個 A 的實例,添加到三級緩存並暴露 Bean

  2. 這時候通過 @Autowired 依賴注入 BeanB 的時候,也就是 getBean(B.class) 的時候,Spring 發現 B 在一級緩存找不到,於是 B 又重複第一步 A 的步驟,創建這個 B 的實例,添加到三級緩存並暴露 Bean

  3. B 通過 @Autowired 依賴注入 BeanA 的時候,這時候發現三級緩存找到了 A 的實例,這時候 A 如果實現 AOP 會先創建動態代理對象,然後把 A 從三級緩存移入了二級緩存中,至此 B 依賴注入成功,並完成了初始化工作,隨即 Spring 把 B 從三級緩存移到了一級緩存裏頭,B 就完成了整個的初始化工作

  4. 最後 Spring 加到了 A 的創建週期,A 依賴注入 B 也就完成了,然後繼續完成 A 的初始化工作,隨即把 A 從二級緩存移到一級緩存

這裏我們可以做一個總結:

如果還是不理解,可以多看幾遍這個動圖:

還是不夠深刻的話,建議下載 Spring 源碼看。

一句話概括:先讓最底層對象完成初始化,通過三級緩存與二級緩存提前曝光創建中的 Bean,讓其他 Bean 率先完成初始化。

五問:Spring 在哪些情況下是無法解決循環依賴問題的呢?

有四種情況是無法解決的:

  1. 多實例 Bean 通過 Setter 注入

  2. 通過構造器注入 Bean

  3. 單例的代理 Bean 通過 Setter 注入

  4. 設置 @DependsOn 註解的 Bean

0x2

寫在最後

其實,很多同學回答這個問題的時候也都知道,Spring 就是使用三級緩存來處理這個問題,但是回答這個問題的難點在於,很多同學對這個問題理解不透,所以當你想要在短時間內向面試官解釋清楚這個問題的時候,就會覺得有點條理不清。

以上這些都是從底層原理昇華而來的具體問題,通常互聯網大廠面試都是按這個套路,包括說很多基礎知識像 JVM、NIO、MQ 等等這些也都是一樣的——面試官先從框架的工作原理入手,考察你對框架原理的理解,最後再找一些實際的問題,考你對這種技術棧的綜合的掌握能力,能不能解決實際的問題。

OK,回答好這個問題,關乎於你對 Spring 框架是否有深刻的理解,相信大家再次總結完後會有所收穫~

作者:Jensen

專注分享程序員日常 / 架構技術 / 職場乾貨

八年 Java 老兵,小米主題設計師,手機輸入法設計師,ProcessOn 特邀講師

曾涉獵航空、電信、IoT、垂直電商產品研發,現就職於某知名電商企業

個人微信:Jensvn,交個朋友,一起成長~

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/t2AionC3EdmgaOBOIfgtCA