spring學習二-spring三級緩存和循環依賴

1 循環依賴介紹

循環依賴是指兩個或兩個以上bean互相持有對方最終形成閉環。比如A依賴B,B依賴C,C依賴A


循環依賴包括構造器依賴和屬性依賴

2 三級緩存解決循環依賴

2.1 spring創建Bean步驟

spring創建bean主要有3個步驟
1 createBeanInstance(實例化bean)
2 populateBean(裝配bean)
3 initializeBean(初始化bean)
發生循環依賴的時候主要是在第2步

2.2 三級緩存介紹

spring管理的對象默認是單例的,那麼肯定有一個地方來緩存這些對象。spring是通過三級緩存來緩存對象的

/** 一級緩存:bean name和bean實例的緩存 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** 三級緩存:對象工廠緩存 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** 二級緩存:提前早期對象的緩存,還沒完成初始化 */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

實例化bean之後會先走如下代碼

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

這段代碼的作用是把實例化的bean從二級緩存移除,放到三級緩存

獲取bean的代碼

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 從一級緩存中獲取對象
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 如果一級緩存沒有並且這個對象正在創建則從二級緩存獲取
        synchronized (this.singletonObjects) {
            // 從二級緩存獲取bean
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 如果二級緩存也沒有則從三級緩存獲取
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 如果從三級緩存獲取成功則把bean從三級緩存移到二級緩存
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

當bean初始化完全後會放入一級緩存

2.3 三級緩存解決循環依賴

假如有一個場景A引用B,B引用A,三級緩存工作原理如下
1 實例化A對象
2 把還未完全初始化的A暴露到三級緩存singletonFactories
3 裝配bean
4 發現依賴B,就get B
5 B還沒有被創建,則進行創建
6 B依賴A,所以get A,由於第二步已經把A放到三級緩存,所以順利地拿到A,完成初始化,把自己放到一級緩存中
7 A順利地拿到B,完成初始化。
三級緩存只能解決通過屬性注入的循環依賴,不能解決通過構造函數注入的循環依賴。因爲把bean放入三級緩存的前提是執行構造函數

2.4 三級緩存的必要性

如果只解決循環依賴問題,一級緩存足矣。但是如果僅僅使用一級緩存,那麼該緩存裏存放的對象既有完全初始化的,又有不完全初始化的。
如果拿到不完全初始化的對象很容易出現NPE
有人說,那好,我再建一個緩存,一級緩存用來存完全初始化的bena,二級緩存用來存不完全初始化的bean,可以了吧。這樣是可以的,但是如果
一個對象被做成切面,那麼該對象就會生成一個代理對象。這樣依賴注入的bean仍是原始的bean,spring會拋異常。
爲了解決代理對象注入的問題,加個三級緩存,裏面不存具體的bean,裏面存一個工廠對象。通過工廠對象,是可以拿到最終形態的代理後的bean
如果想深入瞭解三級緩存的必要性可以參考這位大佬的文章:spring解決循環依賴爲何用三級緩存

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章