天天看點

Spring循環依賴底層實作原理深度剖析

作者:馬士兵程式員

在 Spring 中,循環依賴(Circular Dependency)指的是兩個或多個 Bean 之間互相依賴,形成了一個循環依賴的關系。當出現循環依賴時,Spring 需要通過一些特殊的技術手段來解決這個問題,保證 Bean 的正确建立和初始化。下面我們一起通過理論再結合源碼一起推導出spring循環依賴底層真想。達到對Spring 循環依賴的底層實作原理的深度剖析。

1. 循環依賴的問題

循環依賴問題的出現原因是因為在建立 Bean 的過程中,Bean 之間互相依賴,導緻 Bean 的建立順序不确定,進而無法保證所有的 Bean 都被正确地建立和初始化。例如,假設有兩個 Bean A 和 B,它們都需要依賴對方才能完成初始化:

  • 我們先通過一個簡單示例引出問題
@Component
public class A {
    private B b;

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

@Component
public class B {
    private A a;

    public B(A a) {
        this.a = a;
    }
}
複制代碼           

在這種情況下,如果直接使用 Spring 的标準依賴注入方式,建立 A 和 B 的時候會出現循環依賴的問題,導緻程式無法正确運作。

2. 解決循環依賴的方法

Spring 解決循環依賴的方法是通過提前暴露半成品對象(Early-Stage Object)來解決。當 Spring 建立一個 Bean 的時候,它會先建立該 Bean 的半成品對象,然後再注入該 Bean 所依賴的其他 Bean。當所有的 Bean 都被建立并注入完成後,Spring 再完成這些半成品對象的初始化,進而解決了循環依賴的問題。

Spring 解決循環依賴的過程大緻分為三個步驟:

建立 Bean 的半成品對象,并将其添加到緩存中。 注入該 Bean 所依賴的其他 Bean。 完成 Bean 的初始化,将半成品對象轉換為完整的 Bean 對象。

下面是 Spring 解決循環依賴的具體實作原理。

3. Spring 循環依賴的實作原理

Spring 的循環依賴解決方案主要依賴于兩個技術:BeanPostProcessor 和三級緩存。

BeanPostProcessor

BeanPostProcessor 是 Spring 中的一個接口,它提供了兩個方法:postProcessBeforeInitialization 和 postProcessAfterInitialization。這兩個方法分别在 Bean 的初始化前後被調用,可以用來對 Bean 進行定制化處理。

在解決循環依賴問題時,Spring 使用 BeanPostProcessor 在 Bean 初始化之前對 Bean 進行處理,進而實作提前暴露半成品對象的目的。

- 三級緩存

Spring 中的 BeanFactory 是一個三級緩存結構,其中包含了singletonObjects、earlySingletonObjects 和 singletonFactories 三個緩存。

當 Spring 建立一個 Bean 的時候,它會先檢查 singletonObjects 緩存中是否存在該 Bean 的執行個體。如果存在,直接傳回該執行個體;否則繼續建立該 Bean 的執行個體。

如果在建立該 Bean 的過程中出現了循環依賴,Spring 會将該 Bean 的半成品對象存儲在 earlySingletonObjects 緩存中,并将其标記為“目前正在建立的 Bean”,然後繼續建立該 Bean 所依賴的其他 Bean。當所有的 Bean 都被建立完成後,Spring 會調用 BeanPostProcessor 的 postProcessAfterInitialization 方法,将所有标記為“目前正在建立的 Bean”的半成品對象轉化為完整的 Bean 對象,并存儲在 singletonObjects 緩存中。

如果在建立該 Bean 的過程中需要調用其他 Bean 的工廠方法,則 Spring會将該 Bean 的工廠方法存儲在 singletonFactories 緩存中,以便在建立其他 Bean 時使用。當所有的 Bean 都被建立完成後,Spring 會周遊 singletonFactories 緩存中的所有工廠方法,調用它們的 getObject() 方法,将其轉換為完整的 Bean 對象,并存儲在 singletonObjects 緩存中。

通過使用三級緩存和 BeanPostProcessor,Spring 能夠在 Bean 的建立過程中解決循環依賴問題,并保證所有的 Bean 都被正确地建立和初始化。

4. 循環依賴的限制

雖然 Spring 能夠解決循環依賴問題,但是它也有一些限制:

  • 循環依賴隻适用于 singleton 作用域的 Bean。對于 prototype 作用域的 Bean,Spring 無法解決循環依賴問題。
  • 循環依賴隻适用于 constructor 和 setter 注入方式。對于其他的注入方式,如字段注入或方法注入,Spring 無法解決循環依賴問題。

總之,Spring 的循環依賴解決方案是通過 BeanPostProcessor 和三級緩存實作的。當出現循環依賴時,Spring 會先建立 Bean 的半成品對象,并将其添加到 earlySingletonObjects 緩存中,然後繼續建立該 Bean 所依賴的其他 Bean。當所有的 Bean 都被建立完成後,Spring 會調用 BeanPostProcessor 的 postProcessAfterInitialization 方法,将所有标記為“目前正在建立的 Bean”的半成品對象轉化為完整的 Bean 對象,并存儲在 singletonObjects 緩存中。雖然 Spring 能夠解決循環依賴問題,但是它也有一些限制,需要在使用時注意。

此外,需要注意的是,循環依賴可能會導緻性能問題。當存在大量的循環依賴關系時,Spring 需要建立大量的半成品對象和緩存,進而占用大量的記憶體和 CPU 資源。是以,在設計應用程式時,需要盡量避免循環依賴的出現。

最後,如果出現循環依賴的問題,建議通過重構代碼的方式來解決,盡量減少 Bean 之間的互相依賴關系。如果無法避免循環依賴,可以考慮使用其他依賴注入架構,或者手動管理 Bean 的建立和初始化過程,以避免循環依賴的問題。

好的,下面我們來看一下 Spring 源碼中是如何解決循環依賴的問題的。

5.源碼解析

好的,下面是對 Spring 源碼中解決循環依賴問題的關鍵部分進行注釋的代碼:

// AbstractApplicationContext.java
public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext, DisposableBean {

    public void refresh() throws BeansException, IllegalStateException {
        // 建立 BeanFactory,并通過 BeanFactoryPostProcessor 對 BeanFactory 進行處理
        refreshBeanFactory();
        // 通過 BeanDefinitionReader 将 Bean 的定義資訊讀取到 BeanFactory 中,并注冊到 BeanDefinitionMap 中
        // 然後,通過 DefaultListableBeanFactory 中的 preInstantiateSingletons() 方法建立 Bean 執行個體
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        // ...
        // 建立 Bean 的過程是由 getBean() 方法觸發的
        // 在 getBean() 方法中,會先檢查 singletonObjects 緩存中是否存在該 Bean 的執行個體
        // 如果存在,直接傳回該執行個體;否則繼續建立該 Bean 的執行個體
        beanFactory.getBean(beanName);
        // ...
    }
}

// DefaultListableBeanFactory.java
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry {

    public Object getBean(String name) throws BeansException {
        // ...
        // 先檢查 singletonObjects 緩存中是否存在該 Bean 的執行個體
        // 如果存在,直接傳回該執行個體;否則繼續建立該 Bean 的執行個體
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null) {
            // ...
            return null;
        }
        // ...
        // 建立該 Bean 的半成品對象,并将其存儲在 earlySingletonObjects 緩存中
        // 然後,繼續建立該 Bean 所依賴的其他 Bean
        // 當所有的 Bean 都被建立完成後,會調用 BeanPostProcessor 的 postProcessAfterInitialization() 方法
        // 将所有标記為“目前正在建立的 Bean”的半成品對象轉化為完整的 Bean 對象,并存儲在 singletonObjects 緩存中
        createBean(beanName, mbd, args);
        // ...
    }

    protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) {
        // ...
        // 建立該 Bean 的半成品對象,并将其存儲在 earlySingletonObjects 緩存中
        Object beanInstance = doCreateBean(beanName, mbd, args);
        // ...
        // 調用 BeanPostProcessor 的 postProcessAfterInitialization() 方法
        // 将所有标記為“目前正在建立的 Bean”的半成品對象轉化為完整的 Bean 對象,并存儲在 singletonObjects 緩存中
        // 在 postProcessAfterInitialization() 方法中,會檢查該 Bean 是否存在循環依賴的問題
        Object exposedObject = bean;
        if (mbd.isSingleton()) {
            // ...
            // 将該 Bean 存儲在 singletonObjects 緩存中
            addSingleton(beanName, singletonObject);
            // ...
        }
        // ...
    }

    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
        // ...
        // 如果建立依賴的 Bean 時發現循環依賴的問題
        // 會先将該 Bean 的工廠方法存儲在 singletonFactories 緩存中,以便在建立其他 Bean 時使用
        if (mbd.isPrototype()) {
            // ...
        } else {
            // ...
            // 如果存在循環依賴的問題
            if (isSingletonCurrentlyInCreation(beanName)) {
                // 先從 singletonFactories 緩存中擷取該 Bean 的工廠方法
                ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // ...
                    // 将該 Bean 存儲在 singletonObjects 緩存中
                    addSingleton(beanName, singletonObject);
                    // ...
                }
            }
            // ...
        }
        // ...
    }

    protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
        // ...
        // 在對屬性進行指派的過程中,如果發現循環依賴的問題
        // 會先将該 Bean 的工廠方法存儲在 singletonFactories 緩存中
        if (hasInstantiationAwareBeanPostProcessors()) {
            for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
   
                // 如果該 Bean 存在循環依賴的問題
                // 則先将該 Bean 的工廠方法存儲在 singletonFactories 緩存中
                // 然後,将該 Bean 标記為“目前正在建立的 Bean”
                // 最後,調用 BeanPostProcessor 的 postProcessBeforeInstantiation() 方法
                // 建立該 Bean 的工廠方法,并将其存儲在 singletonFactories 緩存中
                // 在 postProcessBeforeInstantiation() 方法中,會傳回該 Bean 的工廠方法
                // 以便在建立其他 Bean 時使用
                Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);
                if (result != null) {
                    // ...
                    // 将該 Bean 的工廠方法存儲在 singletonFactories 緩存中
                    singletonFactories.put(beanName, () -> result);
                    // ...
                    return result;
                }
            }
        }
        // ...
    }

    protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
        // ...
        // 在對屬性進行指派的過程中,如果發現循環依賴的問題
        // 會先将該 Bean 的工廠方法存儲在 singletonFactories 緩存中
        if (hasInstantiationAwareBeanPostProcessors()) {
            for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
                // ...
                // 如果該 Bean 存在循環依賴的問題
                // 則先将該 Bean 的工廠方法存儲在 singletonFactories 緩存中
                // 然後,将該 Bean 标記為“目前正在建立的 Bean”
                // 最後,調用 BeanPostProcessor 的 postProcessPropertyValues() 方法
                // 對該 Bean 的屬性進行指派,并傳回新的 PropertyValues 對象
                // 在 postProcessPropertyValues() 方法中,會檢查該 Bean 是否存在循環依賴的問題
                PropertyValues pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bean, beanName);
                if (pvsToUse == null) {
                    // ...
                } else {
                    // ...
                    pvs = pvsToUse;
                }
            }
        }
        // ...
    }
}
複制代碼           

6.總結

  1. 在 Spring 中,建立 Bean 的過程是由 AbstractApplicationContext 類中的 refresh() 方法觸發的。在 refresh() 方法中,會先建立 BeanFactory,并通過 BeanFactoryPostProcessor 對 BeanFactory 進行處理。然後,通過 BeanDefinitionReader 将 Bean 的定義資訊讀取到 BeanFactory 中,并注冊到 BeanDefinitionMap 中。接着,通過 DefaultListableBeanFactory 中的 preInstantiateSingletons() 方法建立 Bean 執行個體。
  2. 在 DefaultListableBeanFactory 中,Bean 的建立過程是由 getBean() 方法觸發的。在 getBean() 方法中,會先檢查 singletonObjects 緩存中是否存在該 Bean 的執行個體。如果存在,直接傳回該執行個體;否則繼續建立該 Bean 的執行個體。在建立該 Bean 的過程中,如果存在循環依賴的問題,會先建立 Bean 的半成品對象,并将其存儲在 earlySingletonObjects 緩存中。然後,繼續建立該 Bean 所依賴的其他 Bean。當所有的 Bean 都被建立完成後,會調用 SmartInstantiationAwareBeanPostProcessor 的 postProcessAfterInitialization() 方法,将所有标記為“目前正在建立的 Bean”的半成品對象轉化為完整的 Bean 對象,并存儲在 singletonObjects 緩存中。
  3. 在轉化半成品對象為完整的 Bean 對象的過程中,Spring 會通過 BeanWrapperImpl 中的 setPropertyValue() 方法為該 Bean 的屬性指派。在對屬性進行指派的過程中,如果存在依賴關系,會調用 getBean() 方法建立依賴的 Bean。如果建立依賴的 Bean 時發現循環依賴的問題,會先從 singletonFactories 緩存中擷取該 Bean 的工廠方法,并将其存儲在 singletonFactories 緩存中。然後,繼續建立該 Bean 所依賴的其他 Bean。當所有的 Bean 都被建立完成後,會周遊 singletonFactories 緩存中的所有工廠方法,調用其 getObject() 方法,将其轉換為完整的 Bean 對象,并存儲在 singletonObjects 緩存中。

通過源碼剖析,我們可以看出,Spring 解決循環依賴問題的核心是通過三級緩存和 BeanPostProcessor 實作的。

在建立 Bean 的過程中,如果出現循環依賴的問題,會先建立 Bean 的半成品對象,并将其存儲在 earlySingletonObjects 緩存中。 繼續建立該 Bean 所依賴的其他 Bean。當所有的 Bean 都被建立完成後,會調用 BeanPostProcessor 的 postProcessAfterInitialization() 方法,将所有标記為“目前正在建立的 Bean”的半成品對象轉化為完整的 Bean 對象,并存儲在 singletonObjects 緩存中。 在屬性指派的過程中,如果發現循環依賴的問題,會先将該 Bean 的工廠方法存儲在 singletonFactories 緩存中,以便在建立其他 Bean 時使用。當所有的 Bean 都被建立完成後,會周遊 singletonFactories 緩存中的所有工廠方法,調用其 getObject() 方法,将其轉換為完整的 Bean 對象,并存儲在 singletonObjects 緩存中。

Spring循環依賴底層實作原理深度剖析

作者:算子

連結:https://juejin.cn/post/7220962923735171132

繼續閱讀