AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當前位置:首頁 > 科技  > 軟件

高頻面試:Spring 如何解決循環依賴?

來源: 責編: 時間:2023-10-08 07:05:49 262觀看
導讀Spring 如何解決循環依賴,網上的資料很多,但是感覺寫得好的極少,特別是源碼解讀方面,我就自己單獨出一篇,這篇文章絕對肝!不 BB,上文章目錄。圖片1. 基礎知識1.1 什么是循環依賴 ?一個或多個對象之間存在直接或間接的依賴關系

Spring 如何解決循環依賴,網上的資料很多,但是感覺寫得好的極少,特別是源碼解讀方面,我就自己單獨出一篇,這篇文章絕對肝!P4o28資訊網——每日最新資訊28at.com

不 BB,上文章目錄。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

1. 基礎知識

1.1 什么是循環依賴 ?

一個或多個對象之間存在直接或間接的依賴關系,這種依賴關系構成一個環形調用,有下面 3 種方式。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

我們看一個簡單的 Demo,對標“情況 2”。P4o28資訊網——每日最新資訊28at.com

@Servicepublic class Louzai1 {    @Autowired    private Louzai2 louzai2;    public void test1() {    }}@Servicepublic class Louzai2 {    @Autowired    private Louzai1 louzai1;    public void test2() {    }}

這是一個經典的循環依賴,它能正常運行,后面我們會通過源碼的角度,解讀整體的執行流程。P4o28資訊網——每日最新資訊28at.com

1.2 三級緩存

解讀源碼流程之前,spring 內部的三級緩存邏輯必須了解,要不然后面看代碼會蒙圈。P4o28資訊網——每日最新資訊28at.com

  • 第一級緩存:singletonObjects,用于保存實例化、注入、初始化完成的 bean 實例;
  • 第二級緩存:earlySingletonObjects,用于保存實例化完成的 bean 實例;
  • 第三級緩存:singletonFactories,用于保存 bean 創建工廠,以便后面有機會創建代理對象。

這是最核心,我們直接上源碼:P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

執行邏輯:P4o28資訊網——每日最新資訊28at.com

  • 先從“第一級緩存”找對象,有就返回,沒有就找“二級緩存”;
  • 找“二級緩存”,有就返回,沒有就找“三級緩存”;
  • 找“三級緩存”,找到了,就獲取對象,放到“二級緩存”,從“三級緩存”移除。

1.3 原理執行流程

我把“情況 2”執行的流程分解為下面 3 步,是不是和“套娃”很像 ?P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

整個執行邏輯如下:P4o28資訊網——每日最新資訊28at.com

  1. 在第一層中,先去獲取 A 的 Bean,發現沒有就準備去創建一個,然后將 A 的代理工廠放入“三級緩存”(這個 A 其實是一個半成品,還沒有對里面的屬性進行注入),但是 A 依賴 B 的創建,就必須先去創建 B;
  2. 在第二層中,準備創建 B,發現 B 又依賴 A,需要先去創建 A;
  3. 在第三層中,去創建 A,因為第一層已經創建了 A 的代理工廠,直接從“三級緩存”中拿到 A 的代理工廠,獲取 A 的代理對象,放入“二級緩存”,并清除“三級緩存”;
  4. 回到第二層,現在有了 A 的代理對象,對 A 的依賴完美解決(這里的 A 仍然是個半成品),B 初始化成功;
  5. 回到第一層,現在 B 初始化成功,完成 A 對象的屬性注入,然后再填充 A 的其它屬性,以及 A 的其它步驟(包括 AOP),完成對 A 完整的初始化功能(這里的 A 才是完整的 Bean)。
  6. 將 A 放入“一級緩存”。

為什么要用 3 級緩存 ?我們先看源碼執行流程,后面我會給出答案。P4o28資訊網——每日最新資訊28at.com

2. 源碼解讀

注意:Spring 的版本是 5.2.15.RELEASE,否則和我的代碼不一樣!!!P4o28資訊網——每日最新資訊28at.com

上面的知識,網上其實都有,下面才是我們的重頭戲,讓你跟著樓仔,走一遍代碼流程。P4o28資訊網——每日最新資訊28at.com

2.1 代碼入口

圖片圖片P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

這里需要多跑幾次,把前面的 beanName 跳過去,只看 louzai1。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

2.2 第一層

圖片圖片P4o28資訊網——每日最新資訊28at.com

進入 doGetBean(),從 getSingleton() 沒有找到對象,進入創建 Bean 的邏輯。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

進入 doCreateBean() 后,調用 addSingletonFactory()。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

往三級緩存 singletonFactories 塞入 louzai1 的工廠對象。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

進入到 populateBean(),執行 postProcessProperties(),這里是一個策略模式,找到下圖的策略對象。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

正式進入該策略對應的方法。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

下面都是為了獲取 louzai1 的成員對象,然后進行注入。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

進入 doResolveDependency(),找到 louzai1 依賴的對象名 louzai2P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

需要獲取 louzai2 的 bean,是 AbstractBeanFactory 的方法。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

正式獲取 louzai2 的 bean。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

到這里,第一層套娃基本結束,因為 louzai1 依賴 louzai2,下面我們進入第二層套娃。P4o28資訊網——每日最新資訊28at.com

2.3 第二層

圖片圖片P4o28資訊網——每日最新資訊28at.com

獲取 louzai2 的 bean,從 doGetBean(),到 doResolveDependency(),和第一層的邏輯完全一樣,找到 louzai2 依賴的對象名 louzai1。P4o28資訊網——每日最新資訊28at.com

前面的流程全部省略,直接到 doResolveDependency()。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

正式獲取 louzai1 的 bean。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

到這里,第二層套娃結束,因為 louzai2 依賴 louzai1,所以我們進入第三層套娃。P4o28資訊網——每日最新資訊28at.com

2.4 第三層

圖片圖片P4o28資訊網——每日最新資訊28at.com

獲取 louzai1 的 bean,在第一層和第二層中,我們每次都會從 getSingleton() 獲取對象,但是由于之前沒有初始化 louzai1 和 louzai2 的三級緩存,所以獲取對象為空。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

圖片P4o28資訊網——每日最新資訊28at.com

敲重點!敲重點!!敲重點!!!P4o28資訊網——每日最新資訊28at.com

到了第三層,由于第三級緩存有 louzai1 數據,這里使用三級緩存中的工廠,為 louzai1 創建一個代理對象,塞入二級緩存。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

這里就拿到了 louzai1 的代理對象,解決了 louzai2 的依賴關系,返回到第二層。P4o28資訊網——每日最新資訊28at.com

2.5 返回第二層

返回第二層后,louzai2 初始化結束,這里就結束了么?二級緩存的數據,啥時候會給到一級呢?P4o28資訊網——每日最新資訊28at.com

甭著急,看這里,還記得在 doGetBean() 中,我們會通過 createBean() 創建一個 louzai2 的 bean,當 louzai2 的 bean 創建成功后,我們會執行 getSingleton(),它會對 louzai2 的結果進行處理。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

我們進入 getSingleton(),會看到下面這個方法。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

這里就是處理 louzai2 的 一、二級緩存的邏輯,將二級緩存清除,放入一級緩存。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

2.6 返回第一層

同 2.5,louzai1 初始化完畢后,會把 louzai1 的二級緩存清除,將對象放入一級緩存。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

到這里,所有的流程結束,我們返回 louzai1 對象。P4o28資訊網——每日最新資訊28at.com

3. 原理深度解讀

3.1 什么要有 3 級緩存 ?

這是一道非常經典的面試題,前面已經告訴大家詳細的執行流程,包括源碼解讀,但是沒有告訴大家為什么要用 3 級緩存?P4o28資訊網——每日最新資訊28at.com

這里是重點!敲黑板!!!P4o28資訊網——每日最新資訊28at.com

我們先說“一級緩存”的作用,變量命名為 singletonObjects,結構是 Map<String, Object>,它就是一個單例池,將初始化好的對象放到里面,給其它線程使用,如果沒有第一級緩存,程序不能保證 Spring 的單例屬性。P4o28資訊網——每日最新資訊28at.com

“二級緩存”先放放,我們直接看“三級緩存”的作用,變量命名為 singletonFactories,結構是 Map<String, ObjectFactory<?>>,Map 的 Value 是一個對象的代理工廠,所以“三級緩存”的作用,其實就是用來存放對象的代理工廠。P4o28資訊網——每日最新資訊28at.com

那這個對象的代理工廠有什么作用呢,我先給出答案,它的主要作用是存放半成品的單例 Bean,目的是為了“打破循環”,可能大家還是不太懂,這里我再稍微解釋一下。P4o28資訊網——每日最新資訊28at.com

我們回到文章開頭的例子,創建 A 對象時,會把實例化的 A 對象存入“三級緩存”,這個 A 其實是個半成品,因為沒有完成依賴屬性 B 的注入,所以后面當初始化 B 時,B 又要去找 A,這時就需要從“三級緩存”中拿到這個半成品的 A(這里描述,其實也不完全準確,因為不是直接拿,為了讓大家好理解,我就先這樣描述),打破循環。P4o28資訊網——每日最新資訊28at.com

那我再問一個問題,為什么“三級緩存”不直接存半成品的 A,而是要存一個代理工廠呢 ?答案是因為 AOP。P4o28資訊網——每日最新資訊28at.com

在解釋這個問題前,我們看一下這個代理工廠的源碼,讓大家有一個更清晰的認識。P4o28資訊網——每日最新資訊28at.com

直接找到創建 A 對象時,把實例化的 A 對象存入“三級緩存”的代碼,直接用前面的兩幅截圖。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

下面我們主要看這個對象工廠是如何得到的,進入 getEarlyBeanReference() 方法。P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

圖片P4o28資訊網——每日最新資訊28at.com

圖片圖片P4o28資訊網——每日最新資訊28at.com

最后一幅圖太重要了,我們知道這個對象工廠的作用:P4o28資訊網——每日最新資訊28at.com

  • 如果 A 有 AOP,就創建一個代理對象;
  • 如果 A 沒有 AOP,就返回原對象。

那“二級緩存”的作用就清楚了,就是用來存放對象工廠生成的對象,這個對象可能是原對象,也可能是個代理對象。P4o28資訊網——每日最新資訊28at.com

我再問一個問題,為什么要這樣設計呢?把二級緩存干掉不行么 ?我們繼續往下看。P4o28資訊網——每日最新資訊28at.com

3.2 能干掉第 2 級緩存么 ?

@Servicepublic class A {    @Autowired    private B b;    @Autowired    private C c;    public void test1() {    }}@Servicepublic class B {    @Autowired    private A a;    public void test2() {    }}@Servicepublic class C {    @Autowired    private A a;    public void test3() {    }}

根據上面的套娃邏輯,A 需要找 B 和 C,但是 B 需要找 A,C 也需要找 A。P4o28資訊網——每日最新資訊28at.com

假如 A 需要進行 AOP,因為代理對象每次都是生成不同的對象,如果干掉第二級緩存,只有第一、三級緩存:P4o28資訊網——每日最新資訊28at.com

  • B 找到 A 時,直接通過三級緩存的工廠的代理對象,生成對象 A1。
  • C 找到 A 時,直接通過三級緩存的工廠的代理對象,生成對象 A2。

看到問題沒?你通過 A 的工廠的代理對象,生成了兩個不同的對象 A1 和 A2,所以為了避免這種問題的出現,我們搞個二級緩存,把 A1 存下來,下次再獲取時,直接從二級緩存獲取,無需再生成新的代理對象。P4o28資訊網——每日最新資訊28at.com

所以“二級緩存”的目的是為了避免因為 AOP 創建多個對象,其中存儲的是半成品的 AOP 的單例 bean。P4o28資訊網——每日最新資訊28at.com

如果沒有 AOP 的話,我們其實只要 1、3 級緩存,就可以滿足要求。P4o28資訊網——每日最新資訊28at.com

4. 寫在最后

我們再回顧一下 3 級緩存的作用:P4o28資訊網——每日最新資訊28at.com

  • 一級緩存:為“Spring 的單例屬性”而生,就是個單例池,用來存放已經初始化完成的單例 Bean;
  • 二級緩存:為“解決 AOP”而生,存放的是半成品的 AOP 的單例 Bean;
  • 三級緩存:為“打破循環”而生,存放的是生成半成品單例 Bean 的工廠方法。

如果你能理解上面我說的三條,恭喜你,你對 Spring 的循環依賴理解得非常透徹!P4o28資訊網——每日最新資訊28at.com

關于循環依賴的知識,其實還有,因為篇幅原因,我就不再寫了,這篇文章的重點,一方面是告訴大家循環依賴的核心原理,另一方面是讓大家自己去 debug 代碼,跑跑流程,挺有意思的。P4o28資訊網——每日最新資訊28at.com

可能有同學會問 “樓哥,你之前是不是經常看源碼,然后這個流程,你是不是 debug 了很久?”P4o28資訊網——每日最新資訊28at.com

我之前其實沒怎么看過開源代碼,這個流程,前期理論知識看了 2.5 個小時,然后 debug 4.5 小時,就基本全部走通了,最難的地方,就是三層套娃,稍微有些繞。P4o28資訊網——每日最新資訊28at.com

這里也簡單說一下我看源碼的心得:P4o28資訊網——每日最新資訊28at.com

  1. 需要掌握基本的設計模式;
  2. 看源碼前,最好能找一些理論知識先看看;
  3. 學會讀英文注釋,不會的話就百度翻譯;
  4. debug 時,要克制自己,不要陷入無用的細節,這個最重要。

其中最難的是第 4 步,因為很多同學看 Spring 源碼,每看一個方法,就想多研究研究,這樣很容易被繞進去了,這個要學會克制,有大局觀,并能分辨哪里是核心邏輯,至于如何分辨,可以在網上先找些資料,如果沒有的話,就只能多看代碼了。P4o28資訊網——每日最新資訊28at.com

本文鏈接:http://www.tebozhan.com/showinfo-26-12352-0.html高頻面試:Spring 如何解決循環依賴?

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: JVM 架構—JVM 內部是如何工作的?

下一篇: 徹底搞懂Spring依賴注入(一)Bean實例創建過程

標簽:
  • 熱門焦點
Top