Spring循環(huán)依賴

編碼解決

通過良好的編碼習慣可以避免Spring循環(huán)依賴
方式主要有三種:
1. 構(gòu)造函數(shù)注入: 這是最佳實踐,通過構(gòu)造函數(shù)注入可以避免循環(huán)依賴。在一個類的構(gòu)造函數(shù)中注入其依賴,而不是通過字段注入。

@Service
public class A {
    private final B b;

    @Autowired
    public A(B b) {
        this.b = b;
    }
}

@Service
public class B {
    private final A a;

    @Autowired
    public B(A a) {
        this.a = a;
    }
}

2. Setter方法注入: 使用Setter方法注入可以解決一部分循環(huán)依賴的問題,但不如構(gòu)造函數(shù)注入優(yōu)雅。

@Service
public class A {
    private B b;

    @Autowired
    public void setB(B b) {
        this.b = b;
    }
}

@Service
public class B {
    private A a;

    @Autowired
    public void setA(A a) {
        this.a = a;
    }
}

3. @Lazy注解: 通過在其中一個依賴上使用@Lazy注解,可以延遲初始化被注入的Bean,從而解決循環(huán)依賴。

@Service
public class A {
    @Autowired
    @Lazy
    private B b;
}

@Service
public class B {
    @Autowired
    @Lazy
    private A a;
}

為什么構(gòu)造函數(shù)注入可以解決循環(huán)依賴?

Spring 容器創(chuàng)建一個 Bean 的過程包括以下步驟:

  1. 實例化 Bean 對象: Spring 容器根據(jù)配置或注解等方式,實例化一個 Bean 對象。這一步是通過調(diào)用 Bean 對應類的構(gòu)造函數(shù)完成的。

構(gòu)造函數(shù)注入能夠解決循環(huán)依賴的原因主要是因為在 Java 對象創(chuàng)建的過程中,構(gòu)造函數(shù)是最先被調(diào)用的,而在構(gòu)造函數(shù)中完成了對象的初始化工作。通過構(gòu)造函數(shù)注入,Spring 在創(chuàng)建對象時可以確保依賴的對象已經(jīng)初始化完畢,避免了循環(huán)依賴的問題。

  1. 依賴注入: 容器對 Bean 進行屬性注入。這可以通過構(gòu)造函數(shù)注入、Setter 方法注入或字段注入等方式完成。這是實現(xiàn)控制反轉(zhuǎn)(IoC)的關(guān)鍵步驟,將依賴關(guān)系從應用程序代碼中解耦,由容器負責管理。

因為通過構(gòu)造函數(shù)注入,在依賴注入環(huán)節(jié)需要注入的Bean在上一個實例化階段就初始化完畢了,就不存在循環(huán)依賴問題了

  1. BeanPostProcessor 處理: 如果在容器中注冊了 BeanPostProcessor,Spring 會在實例化 Bean 以及依賴注入之后調(diào)用它們的回調(diào)方法。這提供了一種機制,使開發(fā)者可以在 Bean 實例創(chuàng)建的不同生命周期階段進行定制操作。

  2. 初始化方法調(diào)用: 如果 Bean 實現(xiàn)了 InitializingBean 接口,或者在配置文件中通過 <init-method> 或 @PostConstruct 注解指定了初始化方法,Spring 會在依賴注入之后調(diào)用初始化方法。這個階段是為了讓開發(fā)者執(zhí)行一些初始化邏輯。

  3. BeanPostProcessor 處理(后置初始化): 如果注冊了 BeanPostProcessor,Spring 會在初始化方法調(diào)用后再次調(diào)用它們的回調(diào)方法。這一步稱為后置初始化,提供了對 Bean 進行最后處理的機會。

  4. 將 Bean 放入容器: 容器將創(chuàng)建完成并初始化的 Bean 放入自己的 Bean 容器中,以便供其他 Bean 或組件進行依賴注入。


那三級緩存解決循環(huán)依賴是怎么回事?

Spring 中通過三級緩存(三級Map)的方式來解決循環(huán)依賴問題。這三級緩存的主要作用是在處理 Bean 的創(chuàng)建過程中,記錄 Bean 的創(chuàng)建狀態(tài),避免循環(huán)依賴導致的死循環(huán)。

三級緩存包括:

  1. singletonObjects: 保存完全初始化并準備就緒的單例 Bean。在這個 Map 中,Bean 的名字作為鍵,Bean 實例作為值。在正常情況下,一個單例 Bean 只會放入該緩存一次。

  2. earlySingletonObjects: 保存已經(jīng)實例化但未完成初始化的 Bean。這是為了解決循環(huán)依賴的問題。在這個階段,Bean 實例被提前暴露給其他 Bean,但尚未執(zhí)行完整的初始化。同樣,Bean 的名字作為鍵,Bean 實例作為值。

  3. singletonFactories: 保存 Bean 工廠的提供者,即創(chuàng)建 Bean 的工廠對象。同樣,Bean 的名字作為鍵,Bean 工廠實例作為值。這個階段主要用于處理循環(huán)依賴。

下面是 Spring 處理循環(huán)依賴的基本流程:

  1. 嘗試獲取 Bean:

首先,從 singletonObjects 中嘗試獲取 Bean。如果找到,直接返回。
如果 singletonObjects 中沒有找到,再嘗試從 earlySingletonObjects 中獲取 Bean。如果找到,直接返回。這是為了解決循環(huán)依賴的問題,即使 Bean 尚未完全初始化,也可以提前暴露給其他 Bean 使用。

  1. 創(chuàng)建 Bean:

如果在 singletonObjects 和 earlySingletonObjects 中都沒有找到 Bean,則從 singletonFactories 中獲取 ObjectFactory,然后使用工廠創(chuàng)建 Bean 的原始實例。
在創(chuàng)建過程中,將正在創(chuàng)建的 Bean 放入 earlySingletonObjects。

  1. 初始化 Bean:

完成 Bean 的創(chuàng)建和初始化后,將 Bean 從 earlySingletonObjects 移動到 singletonObjects 中,表示 Bean 創(chuàng)建完成。
所以,earlySingletonObjects 中的 Bean 是在 Bean 創(chuàng)建的過程中,尚未完成初始化的實例,目的是為了提前處理循環(huán)依賴的情況。

總結(jié)起來,earlySingletonObjects 在 Bean 創(chuàng)建的過程中使用,用于解決循環(huán)依賴。在整個生命周期中,Bean 的狀態(tài)會在這三個緩存之間轉(zhuǎn)移。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容