Spring 的循環依賴

前言

記錄 Spring 的一些基本理論,引申出 Spring 循環依賴的問題

Spring 是什麼

是容器(承載各種 bean) 是基石、生態(SpringBoot、SpringCloud 都是在此基礎上的擴展)

Spring 容器(IOC)

本質上就是一個 Map,存放各種 Bean

整體工作流程

此處體現了 Spring 強大的擴展性

SpringContextHolder.getApplicationContext().getBean(xxx.class)

(2) 如子類 BeanFactoryPostProcessor 也是間接實現此接口操作 BeanDefinition

(1) 如 spring.shardingsphere.datasource.master.username={ERP2_MYSQL_USERNAME}

(2) 如 ${jdbc.username}">xml 文件中的 ${jdbc.username}

創建 Bean 對象

放大創建 Bean 對象的流程

* Aware 一個無聲明的接口,Spring 用於標識 bean 是否爲容器對象

此處引出 AOP 的概念:表明 AOP 是 IOC 整體流程中的一個擴展點

Spring 循環依賴的問題

什麼是循環依賴

在上文創建 Bean 對象流程中,放大屬性賦值中自定義屬性賦值流程,假設有這種情況:自定義對象 A 引用了自定義對象 B,自定義對象 B 又引用了自定義對象 A,這種情況稱之爲循環依賴(跟死鎖類似)

#### Spring 解決方式

先將對象按照創建狀態分類:半成品(實例化完成)、成品(初始化完成),不同的狀態存放至不同的 Map 中(三級緩存),後續在判斷容器是否存在 A 對象的時候,不需要去獲取完整的 A 成品對象,只需要獲取 A 半成品對象即可,這也是爲什麼 Spring 會把創建對象分爲實例化和初始化兩個階段來執行的根本原因。

> 有種特殊情況,如果 A 對象中的 b 屬性,是通過構造函授方式注入 ,那麼就是在 A 實例化階段就需要 B 對象了,這種情況就無法解決循環依賴的問題!

三級緩存

本質上就是三個 Map,Srping 用於存放不同創建狀態的 Bean,查看源碼 DefaultSingletonBeanRegistry

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
 ...
 /** Cache of singleton objects: bean name to bean instance. */
 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

 /** Cache of singleton factories: bean name to ObjectFactory. */
 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

 /** Cache of early singleton objects: bean name to bean instance. */
 private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
  ...
}

看流程圖,使用二級緩存,就能解決循環依賴的問題,爲什麼需要用到三級緩存?

此時 AB 對象中各自拿到的就都不是最終版本的 AB(拿到的是普通對象,AOP 失效)

在屬性賦值的時候判斷是否需要將普通對象替換爲代理對象

使用 Lambda 表達式(Lambda 表達式只有在調用的時候纔會執行, 來判斷返回的到底是代理對象還是原始對象) 完整流程

在 A 對象從三級緩存晉升到二級緩存時, 如果判斷 A 對象是需要代理的,則會去提前生成 A 對應的代理對象,替換普通返回。相當於提前執行了 A 的 BeanPostProcessor,而 A 對象在後續的擴展階段也無需再次生成代理類了

最後的問題

如果 A 對象是需要代理的,那麼直接在 earlySingletonObjects 二級緩存中存放代理對象不行嗎,爲什麼要使用 Lambad 函數式接口?

回顧 Bean 的生命週期:設計原則是 Bean 實例化、屬性賦值、初始化之後再去執行 AOP 生成代理對象

但是爲了解決循環依賴但又儘量不打破這個設計原則的情況下,使用了存儲了函數式接口的第三級緩存;如果使用二級緩存的話,可以將 aop 的代理工作提前到屬性賦值階段執行;也就是說所有的 bean 在創建過程中就先生成代理對象再初始化和其他工作;但是這樣的話,就和 spring 的 aop 的設計原則相駁,aop 的實現需要與 bean 的正常生命週期的創建分離;這樣只有使用第三級緩存封裝一個函數式接口對象到緩存中, 發生循環依賴時,再觸發代理類的生成。

source : www.cnblogs.com/xurongze/articles/16275516.html
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/9j-cCKE_9SnGuK3M9LM5bQ