1. 程式人生 > >Spring原始碼分析之迴圈依賴及解決方案

Spring原始碼分析之迴圈依賴及解決方案

# Spring原始碼分析之迴圈依賴及解決方案 往期文章: 1. [Spring原始碼分析之預啟動流程](https://mp.weixin.qq.com/s/bfbPJOlYo2Vz2UTSMWRGkw) 2. [Spring原始碼分析之BeanFactory體系結構](https://mp.weixin.qq.com/s/FDx0hmCp7dEfw5wzhS3fNA) 3. [Spring原始碼分析之BeanFactoryPostProcessor呼叫過程詳解](https://mp.weixin.qq.com/s/gHL6Q0A0xwxSCZ0_hKJhEQ) 4. [Spring原始碼分析之Bean的建立過程詳解](https://mp.weixin.qq.com/s/MLOJzJRFNrEjTqAlr3YCSw) 正文: 首先,我們需要明白什麼是迴圈依賴?簡單來說就是A物件建立過程中需要依賴B物件,而B物件建立過程中同樣也需要A物件,所以A建立時需要先去把B創建出來,但B建立時又要先把A創建出來...死迴圈有木有... ![迴圈依賴](https://img2020.cnblogs.com/other/1187061/202011/1187061-20201116112933811-1778032139.png) 那麼在Spring中,有多少種迴圈依賴的情況呢?大部分人只知道兩個普通的Bean之間的迴圈依賴,而Spring中其實存在三種物件(普通Bean,工廠Bean,代理物件),他們之間都會存在迴圈依賴,這裡我給列舉出來,大致分別以下幾種: - 普通Bean與普通Bean之間 - 普通Bean與代理物件之間 - 代理物件與代理物件之間 - 普通Bean與工廠Bean之間 - 工廠Bean與工廠Bean之間 - 工廠Bean與代理物件之間 那麼,在Spring中是如何解決這個問題的呢? ## 1. 普通Bean與普通Bean 首先,我們先設想一下,如果讓我們自己來編碼,我們會如何解決這個問題? ### 栗子 現在我們有兩個互相依賴的物件A和B ```java public class NormalBeanA { private NormalBeanB normalBeanB; public void setNormalBeanB(NormalBeanB normalBeanB) { this.normalBeanB = normalBeanB; } } ``` ```java public class NormalBeanB { private NormalBeanA normalBeanA; public void setNormalBeanA(NormalBeanA normalBeanA) { this.normalBeanA = normalBeanA; } } ``` 然後我們想要讓他們彼此都含有物件 ```java public class Main { public static void main(String[] args) { // 先建立A物件 NormalBeanA normalBeanA = new NormalBeanA(); // 建立B物件 NormalBeanB normalBeanB = new NormalBeanB(); // 將A物件的引用賦給B normalBeanB.setNormalBeanA(normalBeanA); // 再將B賦給A normalBeanA.setNormalBeanB(normalBeanB); } } ``` 發現了嗎?我們並沒有先建立一個完整的A物件,而是先建立了一個空殼物件(Spring中稱為早期物件),將這個早期物件A先賦給了B,使得得到了一個完整的B物件,再將這個完整的B物件賦給A,從而解決了這個迴圈依賴問題,so easy! 那麼Spring中是不是也這樣做的呢?我們就來看看吧~ ### Spring中的解決方案 > 由於上一篇已經分析過Bean的建立過程了,其中的某些部分就不再細講了 #### 先來到建立Bean的方法 > AbstractAutowireCapableBeanFactory#doCreateBean 假設此時在建立A ```java protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){ // beanName -> A // 例項化A BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args); // 是否允許暴露早期物件 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { // 將獲取早期物件的回撥方法放到三級快取中 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } } ``` addSingletonFactory ```java protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) { synchronized (this.singletonObjects) { // 單例快取池中沒有該Bean if (!this.singletonObjects.containsKey(beanName)) { // 將回調函式放入三級快取 this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } } ``` > ObjectFactory是一個函式式介面 在這裡,我們發現在建立Bean時,Spring不管三七二十一,直接將一個獲取早期物件的回撥方法放進了一個三級快取中,我們再來看一下回調方法的邏輯 getEarlyBeanReference ```java protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; // 呼叫BeanPostProcessor對早期物件進行處理,在Spring的內建處理器中,並無相關的處理邏輯 // 如果開啟了AOP,將引入一個AnnotationAwareAspectJAutoProxyCreator,此時將可能對Bean進行動態代理 if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } } return exposedObject; } ``` > 在這裡,如果沒有開啟AOP,或者該物件不需要動態代理,會直接返回原物件 此時,已經將A的早期物件快取起來了,接下來在填充屬性時會發生什麼呢? 相信大家也應該想到了,A物件填充屬性時必然發現依賴了B物件,此時就將轉頭建立B,在建立B時同樣會經歷以上步驟,此時就該B物件填充屬性了,這時,又將要轉頭建立A,那麼,現在會有什麼不一樣的地方呢?我們看看getBean的邏輯吧 doGetBean ```java pr