日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁 > 科技  > 軟件

高頻面試:Spring 如何解決循環(huán)依賴?

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

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

不 BB,上文章目錄。VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

1. 基礎(chǔ)知識(shí)

1.1 什么是循環(huán)依賴 ?

一個(gè)或多個(gè)對象之間存在直接或間接的依賴關(guān)系,這種依賴關(guān)系構(gòu)成一個(gè)環(huán)形調(diào)用,有下面 3 種方式。VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

我們看一個(gè)簡單的 Demo,對標(biāo)“情況 2”。VWC28資訊網(wǎng)——每日最新資訊28at.com

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

這是一個(gè)經(jīng)典的循環(huán)依賴,它能正常運(yùn)行,后面我們會(huì)通過源碼的角度,解讀整體的執(zhí)行流程。VWC28資訊網(wǎng)——每日最新資訊28at.com

1.2 三級(jí)緩存

解讀源碼流程之前,spring 內(nèi)部的三級(jí)緩存邏輯必須了解,要不然后面看代碼會(huì)蒙圈。VWC28資訊網(wǎng)——每日最新資訊28at.com

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

這是最核心,我們直接上源碼:VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

執(zhí)行邏輯:VWC28資訊網(wǎng)——每日最新資訊28at.com

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

1.3 原理執(zhí)行流程

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

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

整個(gè)執(zhí)行邏輯如下:VWC28資訊網(wǎng)——每日最新資訊28at.com

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

為什么要用 3 級(jí)緩存 ?我們先看源碼執(zhí)行流程,后面我會(huì)給出答案。VWC28資訊網(wǎng)——每日最新資訊28at.com

2. 源碼解讀

注意:Spring 的版本是 5.2.15.RELEASE,否則和我的代碼不一樣?。?!VWC28資訊網(wǎng)——每日最新資訊28at.com

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

2.1 代碼入口

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

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

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

2.2 第一層

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

進(jìn)入 doGetBean(),從 getSingleton() 沒有找到對象,進(jìn)入創(chuàng)建 Bean 的邏輯。VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

進(jìn)入 doCreateBean() 后,調(diào)用 addSingletonFactory()。VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

往三級(jí)緩存 singletonFactories 塞入 louzai1 的工廠對象。VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

進(jìn)入到 populateBean(),執(zhí)行 postProcessProperties(),這里是一個(gè)策略模式,找到下圖的策略對象。VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

正式進(jìn)入該策略對應(yīng)的方法。VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

下面都是為了獲取 louzai1 的成員對象,然后進(jìn)行注入。VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

進(jìn)入 doResolveDependency(),找到 louzai1 依賴的對象名 louzai2VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

需要獲取 louzai2 的 bean,是 AbstractBeanFactory 的方法。VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

正式獲取 louzai2 的 bean。VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

到這里,第一層套娃基本結(jié)束,因?yàn)?louzai1 依賴 louzai2,下面我們進(jìn)入第二層套娃。VWC28資訊網(wǎng)——每日最新資訊28at.com

2.3 第二層

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

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

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

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

正式獲取 louzai1 的 bean。VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

到這里,第二層套娃結(jié)束,因?yàn)?louzai2 依賴 louzai1,所以我們進(jìn)入第三層套娃。VWC28資訊網(wǎng)——每日最新資訊28at.com

2.4 第三層

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

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

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

敲重點(diǎn)!敲重點(diǎn)??!敲重點(diǎn)!?。?span style="display:none">VWC28資訊網(wǎng)——每日最新資訊28at.com

到了第三層,由于第三級(jí)緩存有 louzai1 數(shù)據(jù),這里使用三級(jí)緩存中的工廠,為 louzai1 創(chuàng)建一個(gè)代理對象,塞入二級(jí)緩存。VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

這里就拿到了 louzai1 的代理對象,解決了 louzai2 的依賴關(guān)系,返回到第二層。VWC28資訊網(wǎng)——每日最新資訊28at.com

2.5 返回第二層

返回第二層后,louzai2 初始化結(jié)束,這里就結(jié)束了么?二級(jí)緩存的數(shù)據(jù),啥時(shí)候會(huì)給到一級(jí)呢?VWC28資訊網(wǎng)——每日最新資訊28at.com

甭著急,看這里,還記得在 doGetBean() 中,我們會(huì)通過 createBean() 創(chuàng)建一個(gè) louzai2 的 bean,當(dāng) louzai2 的 bean 創(chuàng)建成功后,我們會(huì)執(zhí)行 getSingleton(),它會(huì)對 louzai2 的結(jié)果進(jìn)行處理。VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

我們進(jìn)入 getSingleton(),會(huì)看到下面這個(gè)方法。VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

這里就是處理 louzai2 的 一、二級(jí)緩存的邏輯,將二級(jí)緩存清除,放入一級(jí)緩存。VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

2.6 返回第一層

同 2.5,louzai1 初始化完畢后,會(huì)把 louzai1 的二級(jí)緩存清除,將對象放入一級(jí)緩存。VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

到這里,所有的流程結(jié)束,我們返回 louzai1 對象。VWC28資訊網(wǎng)——每日最新資訊28at.com

3. 原理深度解讀

3.1 什么要有 3 級(jí)緩存 ?

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

這里是重點(diǎn)!敲黑板?。?!VWC28資訊網(wǎng)——每日最新資訊28at.com

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

“二級(jí)緩存”先放放,我們直接看“三級(jí)緩存”的作用,變量命名為 singletonFactories,結(jié)構(gòu)是 Map<String, ObjectFactory<?>>,Map 的 Value 是一個(gè)對象的代理工廠,所以“三級(jí)緩存”的作用,其實(shí)就是用來存放對象的代理工廠。VWC28資訊網(wǎng)——每日最新資訊28at.com

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

我們回到文章開頭的例子,創(chuàng)建 A 對象時(shí),會(huì)把實(shí)例化的 A 對象存入“三級(jí)緩存”,這個(gè) A 其實(shí)是個(gè)半成品,因?yàn)闆]有完成依賴屬性 B 的注入,所以后面當(dāng)初始化 B 時(shí),B 又要去找 A,這時(shí)就需要從“三級(jí)緩存”中拿到這個(gè)半成品的 A(這里描述,其實(shí)也不完全準(zhǔn)確,因?yàn)椴皇侵苯幽?,為了讓大家好理解,我就先這樣描述),打破循環(huán)。VWC28資訊網(wǎng)——每日最新資訊28at.com

那我再問一個(gè)問題,為什么“三級(jí)緩存”不直接存半成品的 A,而是要存一個(gè)代理工廠呢 ?答案是因?yàn)?AOP。VWC28資訊網(wǎng)——每日最新資訊28at.com

在解釋這個(gè)問題前,我們看一下這個(gè)代理工廠的源碼,讓大家有一個(gè)更清晰的認(rèn)識(shí)。VWC28資訊網(wǎng)——每日最新資訊28at.com

直接找到創(chuàng)建 A 對象時(shí),把實(shí)例化的 A 對象存入“三級(jí)緩存”的代碼,直接用前面的兩幅截圖。VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

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

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片VWC28資訊網(wǎng)——每日最新資訊28at.com

最后一幅圖太重要了,我們知道這個(gè)對象工廠的作用:VWC28資訊網(wǎng)——每日最新資訊28at.com

  • 如果 A 有 AOP,就創(chuàng)建一個(gè)代理對象;
  • 如果 A 沒有 AOP,就返回原對象。

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

我再問一個(gè)問題,為什么要這樣設(shè)計(jì)呢?把二級(jí)緩存干掉不行么 ?我們繼續(xù)往下看。VWC28資訊網(wǎng)——每日最新資訊28at.com

3.2 能干掉第 2 級(jí)緩存么 ?

@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() {    }}

根據(jù)上面的套娃邏輯,A 需要找 B 和 C,但是 B 需要找 A,C 也需要找 A。VWC28資訊網(wǎng)——每日最新資訊28at.com

假如 A 需要進(jìn)行 AOP,因?yàn)榇韺ο竺看味际巧刹煌膶ο?,如果干掉第二?jí)緩存,只有第一、三級(jí)緩存:VWC28資訊網(wǎng)——每日最新資訊28at.com

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

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

所以“二級(jí)緩存”的目的是為了避免因?yàn)?AOP 創(chuàng)建多個(gè)對象,其中存儲(chǔ)的是半成品的 AOP 的單例 bean。VWC28資訊網(wǎng)——每日最新資訊28at.com

如果沒有 AOP 的話,我們其實(shí)只要 1、3 級(jí)緩存,就可以滿足要求。VWC28資訊網(wǎng)——每日最新資訊28at.com

4. 寫在最后

我們再回顧一下 3 級(jí)緩存的作用:VWC28資訊網(wǎng)——每日最新資訊28at.com

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

如果你能理解上面我說的三條,恭喜你,你對 Spring 的循環(huán)依賴?yán)斫獾梅浅M笍兀?span style="display:none">VWC28資訊網(wǎng)——每日最新資訊28at.com

關(guān)于循環(huán)依賴的知識(shí),其實(shí)還有,因?yàn)槠颍揖筒辉賹懥耍@篇文章的重點(diǎn),一方面是告訴大家循環(huán)依賴的核心原理,另一方面是讓大家自己去 debug 代碼,跑跑流程,挺有意思的。VWC28資訊網(wǎng)——每日最新資訊28at.com

可能有同學(xué)會(huì)問 “樓哥,你之前是不是經(jīng)??丛创a,然后這個(gè)流程,你是不是 debug 了很久?”VWC28資訊網(wǎng)——每日最新資訊28at.com

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

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

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

其中最難的是第 4 步,因?yàn)楹芏嗤瑢W(xué)看 Spring 源碼,每看一個(gè)方法,就想多研究研究,這樣很容易被繞進(jìn)去了,這個(gè)要學(xué)會(huì)克制,有大局觀,并能分辨哪里是核心邏輯,至于如何分辨,可以在網(wǎng)上先找些資料,如果沒有的話,就只能多看代碼了。VWC28資訊網(wǎng)——每日最新資訊28at.com

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

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: JVM 架構(gòu)—JVM 內(nèi)部是如何工作的?

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

標(biāo)簽:
  • 熱門焦點(diǎn)
Top 主站蜘蛛池模板: 海城市| 剑川县| 临夏县| 通化市| 且末县| 松桃| 蒙自县| 萨迦县| 普兰店市| 泽普县| 新郑市| 普洱| 托克托县| 永顺县| 息烽县| 乌审旗| 高密市| 龙门县| 香港| 色达县| 班玛县| 勃利县| 南召县| 武隆县| 高州市| 徐闻县| 进贤县| 铁岭县| 廊坊市| 扬中市| 太谷县| 大城县| 九龙坡区| 富阳市| 岳阳市| 肇东市| 石城县| 延安市| 桐庐县| 阳谷县| 平安县|