Spring 的循環依賴
前言
記錄 Spring 的一些基本理論,引申出 Spring 循環依賴的問題
Spring 是什麼
是容器(承載各種 bean) 是基石、生態(SpringBoot、SpringCloud 都是在此基礎上的擴展)
Spring 容器(IOC)
本質上就是一個 Map,存放各種 Bean
整體工作流程
-
BeanDefinitionReader 一個接口,約束和規範 bean 信息的定義。其子類是將各種格式的 bean 信息轉換爲 BeanDefinition 的實現
(1) XmlBeanDefinitionReader 轉換 XML
(2) PropertiesBeanDefinitionReader 轉換配置文件(Resource,Property)
(3) GroovyBeanDefinitionReader 轉換 Groovy
此處體現了 Spring 強大的擴展性
-
BeanFactory 訪問 Bean 容器的根接口
(1) 如子類 ApplicationContext
SpringContextHolder.getApplicationContext().getBean(xxx.class)
(2) 如子類 BeanFactoryPostProcessor 也是間接實現此接口操作 BeanDefinition
- BeanFactoryPostProcessor 處理 Bean 定義屬性值中的佔位符
(1) 如 spring.shardingsphere.datasource.master.username={ERP2_MYSQL_USERNAME}
(2) 如 ${jdbc.username}">xml 文件中的 ${jdbc.username}
創建 Bean 對象
放大創建 Bean 對象的流程
-
如想把 ApplicationContext 這種容器對象想作爲自定義 bean 的屬性時,則需實現 ApplicationContextAware(ApplicationContextAware extends Aware)接口,告訴 Spring(讓 Spring 感知),Spring 會在統一的地方處理爲其賦值
-
BeanPostProcessor 做 Bean 對象的擴展實現
(1) 如子類 AbstractAutoProxyCreator 通過動態代理方式實現擴展(AOP)
此處引出 AOP 的概念:表明 AOP 是 IOC 整體流程中的一個擴展點
-
invokeInitMethods 執行 Bean 對象自定義初始化方法
(1) 當校驗 Bean 實現 InitializingBean 接口時,此處會調用 afterPropertiesSet 方法,做一些 bean 使用前的初始化工作
Spring 循環依賴的問題
什麼是循環依賴
在上文創建 Bean 對象流程中,放大屬性賦值中自定義屬性賦值流程,假設有這種情況:自定義對象 A 引用了自定義對象 B,自定義對象 B 又引用了自定義對象 A,這種情況稱之爲循環依賴(跟死鎖類似)
先將對象按照創建狀態分類:半成品(實例化完成)、成品(初始化完成),不同的狀態存放至不同的 Map 中(三級緩存),後續在判斷容器是否存在 A 對象的時候,不需要去獲取完整的 A 成品對象,只需要獲取 A 半成品對象即可,這也是爲什麼 Spring 會把創建對象分爲實例化和初始化兩個階段來執行的根本原因。
三級緩存
本質上就是三個 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);
...
}
-
singletonObjects 一級緩存,存放成品對象(初始化完成)
-
earlySingletonObjects 二級緩存,存放半成品對象(實例化完成)
-
singletonFactories 三級緩存 ,存放創建對象的 Lambda 表達式
- 回顧上面創建 Bean 對象流程,Bean 賦值完成後是爲普通對象,如果對象需要擴展爲 AOP 代理對象呢?
此時 AB 對象中各自拿到的就都不是最終版本的 AB(拿到的是普通對象,AOP 失效)
- 再看創建 Bean 對象流程,代理對象的創建是在屬性賦值階段之後的,如何在屬性賦值階段獲取到代理對象?
在屬性賦值的時候判斷是否需要將普通對象替換爲代理對象
- 如何判斷?
使用 Lambda 表達式(Lambda 表達式只有在調用的時候纔會執行, 來判斷返回的到底是代理對象還是原始對象) 完整流程
- org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference
最後的問題
如果 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