Spring源碼解析——迴圈依賴的解決方案

来源:https://www.cnblogs.com/midoujava/archive/2019/08/02/11291487.html
-Advertisement-
Play Games

一、前言 承接 "《Spring源碼解析——創建bean》" 、 "《Spring源碼解析——創建bean的實例》" ,我們今天接著聊聊,迴圈依賴的解決方案,即創建bean的ObjectFactory。 二、ObjectFactory 這段代碼不是很複雜,但是很多人不是太理解這段代碼的作用,而且,這 ...


關註米兜Java.md

一、前言

承接《Spring源碼解析——創建bean》《Spring源碼解析——創建bean的實例》,我們今天接著聊聊,迴圈依賴的解決方案,即創建bean的ObjectFactory。

二、ObjectFactory

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
        isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
    if (logger.isDebugEnabled()) {
        logger.debug("Eagerly caching bean '" + beanName +
                "' to allow for resolving potential circular references");
    }
    // 為避免後期迴圈依賴,可以在bean初始化完成前將創建實例的ObjectFactory加入工廠
    /**
     * getEarlyBeanReference(beanName, mbd, bean)方法:
     * 對bean再一次依賴引用,主要應用SmartInstantiationAwareBeanPostProcessor
     * 其中我們熟知的AOP就是在這裡將advice動態織入bean中,若沒有則直接返回bean,不做任何處理
     */
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

這段代碼不是很複雜,但是很多人不是太理解這段代碼的作用,而且,這段代碼僅從此函數中去理解也很難弄懂其中的含義,我們需要從全局的角度去思考 Spring 的依賴解決辦法。
earlySingletonExposure :從字面的意思理解就是提早曝光的單例,我們暫不定義它的學名叫什麼,我們感興趣的是有哪些條件影響這個值。

  • mbd.isSingleton() :沒有太多可以解釋的,此 RootBeanDefinition 代表的是否是單例。
  • this.allowCircularReferences :是否允許迴圈依賴,很抱歉,並沒有找到在配置文件中如何配置,但是在 AbstractRefreshableApplicationContext 中提供了設置函數,可以通過硬編碼的方式進行設置或者可以通過自定義命名空間進行配置,其中硬編碼的方式代碼如下。
ClassPathXmlApplicationContext bf = ClassPathXmlApplicationContext("aspectTest.xml" ); bt.setAllowBeanDefinitionOverriding(false);
  • isSingletonCurrentlylncreation(beanName) :該 bean 是否在創建中。在 Spring 中,會有個專門的屬性預設為 DefaultSingletonBeanRegistry的 singletonsCurrentlylnCreation 來記錄 bean 的載入狀態,在 bean 開始創建前會將 beanName 記錄在屬性中,在 bean 創建結束後會將 beanName 從屬性中移除。那麼我們跟隨代碼一路走來可是對這個屬性的記錄並沒有多少印象,這個狀態是在哪裡記錄的呢?不同 scope 的記錄位置並不一樣,我們以 singleton 為例,在 singleton 下記錄屬性的函數是在 DefaultSingletonBeanRegistry的 public Object getSingleton(String beanName, ObjectFactory singletonFactory)函數的 beforeSingletonCreation(beanName)和 afterSingletonCreation(beanName)中,在這兩段函數中分別this.singletonCurrentlylnCreation.add(beanName)與 this.singletonCurrentlylnCreation.remove(beanName)來進行狀態的記錄與移除。

經過以上分析我們瞭解變數 earl earlySingletonExposure 是否是單例、是否允許迴圈依賴、是否對應的 bean 正在創建的條件的綜合。當這 3 個條件都滿足時會執行 addSingletonFactory操作,那麼加入 SingletonFactory的作用是什麼呢?又是在什麼時候調用呢?

我們還是以最簡單的AB迴圈依賴為例,類A中含有屬性類B,而類B中又會含有屬性類A,那麼初始化beanA的過程如下圖所示:
beanA.md

上圖展示了創建 beanA 的流程,圖中我們看到,在創建 A 的時候首先會記錄類 A 所對應的 beanName,並將beanA的創建工廠加入緩存中,而在對 A的屬性填充也就是調用populate方法的時候又會再一次的對 B 進行遞歸創建。同樣的,因為在 B 中同樣存在 A 屬性,因此在實例化 B 的的 populate 方法中又會再次地初始化 A ,也就是圖形的最後,調用 getBean(A)。關鍵是在這裡,有心的同學可以去找找這個代碼的實現方式,我們之前已經講過,在這個函數中並不是直接去實例化 A ,而是先去檢測緩存中是否有已經創建好的對應的 bean ,或者是否已經創建好的 ObjectFactory,而此時對於A的 ObjectFactory我們早已經創建,所以便不會再去向後執行,而是直接調用 ObjectFactory去創建 A 。這裡最關鍵的是 ObjectFactory的實現。

/**
 * getEarlyBeanReference(beanName, mbd, bean)方法:
 * 對bean再一次依賴引用,主要應用SmartInstantiationAwareBeanPostProcessor
 * 其中我們熟知的AOP就是在這裡將advice動態織入bean中,若沒有則直接返回bean,不做任何處理
 */
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

其中getEarlyBeanReference的代碼如下:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

在 getEarlyBeanReference 函數中並沒有太多的邏輯處理,或者說除了後處理器的調用外沒有別的處理工作,根據以上分析,基本可以理清 spring 處理迴圈依賴的解決辦法,在 B 中創建依賴 A 時通過 ObjectFactory 提供的實例化方法來中斷 A 中的屬性填充,使 B 中持有的 A 僅僅是剛剛初始化並沒有填充任何屬性的 A ,而這正初始化 A 的步驟還是在最開始創建 A 的時候進行的,但是因為 A 與 B 中的 A 所表示的屬性地址是一樣的,所以在 A 中創建好的屬性填充自然可以通過 B 中的 A 獲取,這樣就解決了迴圈依賴的問題。

三、小結

大體上的原理就是這樣,有什麼不明白的地方,大伙可繼續閱讀如下文章:

https://blog.csdn.net/m0_38043362/article/details/80284577

https://blog.csdn.net/hzcao/article/details/78479593

http://book.51cto.com/art/201311/419098.htm

本文在米兜公眾號鏈接

https://mp.weixin.qq.com/s/P7f0HLnyjHqoN4-rUm0ytQ

歡迎關註米兜Java,一個註在共用、交流的Java學習平臺。

米兜Java.md


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、activiti app修改數據源和密碼 1、使用sublimetext工具打開tomcat,方便進行配置文件的修改。 找到被解壓的war包,activiti app/WEB INF/classes/META INF/activiti app/activiti app.properties文件, ...
  • 1.安裝JDK ElasticSearch是用JAVA語言開發的,其運行需要安裝JDK。 JDK (Java Development Kit) ,是整個Java的核心,包括了Java運行環境(Java Runtime Envirnment),一堆Java工具和Java基礎的類庫(rt.jar)。 2 ...
  • 序列 序列是Python中最基本的數據結構,包括字元串、列表、元組。 序列,顧名思義,是有序的,序列都有索引,都能進行索引、切片(截取)、加(連接)、乘(倍增)、檢查成員的操作。 因為序列有序,可通過位置來區分元素,所以序列中可含有相同的元素。 序列的通用操作 1、索引 seq[index] ind ...
  • 一、Jupyter安裝 前提需要已經安裝好python環境~ 接著,Python3x版本安裝路徑下執行pip命令安裝 pip3 install Jupyter 看網速,安裝完後會顯示安裝成功一段話即可。 二、啟動jupyter notebook兩種方法 1:命令行視窗輸入jupyter notebo ...
  • 接入支付寶準備工作:(關於賬號可以是個體商戶也可以是企業賬號但必須有營業執照) 1.登錄螞蟻金服開放平臺 2.創建應用,應用分類網頁應用和移動應用。應用提交審核審核通過後得到Appid才能調用相應的介面許可權 3.添加功能:一般有掃碼付,電腦網站支付,手機網站支付,APP支付。看你的需求什麼。移動應用 ...
  • ckeditor作為老牌的優秀線上編輯器,一直受到開發者的青睞。 這裡我們講解下 ckeditor最新版本4.7的圖片上傳配置。 https://ckeditor.com/ 官方 進入下載 https://ckeditor.com/download 我們下載完整版 預設本地上傳沒有開啟; 找到cke ...
  • B篇,主要介紹Python的自定義函數,匿名函數,面向對象,模塊化。 由於不涉及基礎的知識,我會將重難點加以解釋。 ...
  • Ural 1250 Sea Burial 題解 [TOC] 題意 給定一個$n\times m$的地圖,$.$為水,$\ $為陸,地圖的外部是水(地圖被水包圍)。水為八連通,陸為四聯通。聯通的水稱為海,聯通的陸稱為島。海內可能有島,島內可能有海。給定$x,y$求在包含$(x,y)$(保證$(x,y) ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...