淺談Spring 如何解決循環依賴問題

淺談Spring 如何解決循環依賴問題

前言

1、在我們工作中當進行應用開發時,經常會遇到循環依賴的問題,因爲隨着系統業務複雜性的增大,Service調用另一個Service的場景經常出現,所以一不小心就會造成循環依賴的問題,如果循環鏈比較長,那可能要來一場頭腦風暴,因爲這個只有項目啓動時纔會拋出BeanCurrentlyInCreationException異常。
2、當你知道是怎麼回事時,這時你想了解Spring 如何解決循環依賴的問題,於是你下定決心翻出Spring源碼,也許花了很大力氣或者大半天的時間去閱讀,結果還是雨裏霧裏。
3、趁着週末有時間,跟大家分享一下我對Spring 解決循環依賴的理解,如果有什麼不對的地方歡迎大家來信指正。

正文

首先我們來看看什麼是循環依賴,我畫了一張圖來描述:
在這裏插入圖片描述
或者自己依賴自己:
在這裏插入圖片描述
看完上面的圖,我們對循環依賴有所瞭解,針對這個問題,我們下面看看Spring 是怎麼解決的。
其實Spring內部維護了3個緩存,singletonObjects 、singletonFactories 和earlySingletonObjects 。singletonObjects 是緩存單例的地方,singletonFactories 是緩存單例工廠,earlySingletonObjects 是保存Bean早期引用的地方。大概的流程是:當創建A時,提前暴露A工廠,在填充屬性時發現需要注入B,那麼去創建B,提前暴露B工廠,填充屬性時又發現需要注入A,這時因爲A工廠已提前暴露,所以調用A工廠直接返回早期的A實例並緩存到earlySingletonObjects 中。
看完這個描述還是一頭霧水怎麼辦,不急,下面我寫了一段簡單的demo來模仿這一流程。

public class A {
    private B b;
}

public class B {
    private A a;
}

public TestDemo{
	private static Map<String, Object> objectMap = new HashMap<>();

    public static void main(String[] args) {
        getBean(A.class);
        getBean(B.class);
    }

    private static <T> T getBean(Class<T> beanClass) {
        String beanName = beanClass.getSimpleName().toLowerCase();
        // 如果緩存有則直接返回
        if (objectMap.containsKey(beanName)) {
            return (T) objectMap.get(beanName);
        }
        // 實例化
        Object object = beanClass.getDeclaredConstructor().newInstance();
        // 放入緩存
        objectMap.put(beanName, object);
        // 把所有字段當成需要注入的bean,創建並注入到當前bean中
        Field[] fields = object.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            Class<?> fieldClass = field.getType();
            String fieldBeanName = fieldClass.getSimpleName().toLowerCase();
            // 如果需要注入的bean,已經在緩存Map中,那麼把緩存Map中的值注入到該field即可
            // 如果緩存沒有 繼續創建
            field.set(object, objectMap.containsKey(fieldBeanName)
                    ? objectMap.get(fieldBeanName) : getBean(fieldClass));
        }
        // 屬性填充完成,返回
        return (T) object;
    }
 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章