3.4 spring5源碼系列--循環依賴的設計思想


前面已經寫了關於三篇循環依賴的文章, 這是一個總結篇

第一篇: 3.1 spring5源碼系列--循環依賴 之 手寫代碼模擬spring循環依賴

第二篇: 3.2spring源碼系列----循環依賴源碼分析

第三篇: 3.3 Spring5源碼---循環依賴過程中spring讀取不完整bean的最終

現在總結循環依賴的思想

學了那么多, 為什么說見多才能識廣呢 , 知道別人是如何解決某一類問題的, 也就是優秀代碼的魅力. 這也是為什么要學習別人的代碼的原因.

思想才是我們可以在工作中借鑒使用的

1. 循環依賴的三級緩存設計

2. 接口函數

 


 

一. 循環依賴的三級緩存設計

再循環依賴的過程中設計了三級緩存, 他們的作用分別是

1. 一級緩存: 用來存放完整的bean

2. 二級緩存: 用來存放早期的,純凈的bean

3. 三級緩存: 用來存放接口函數.

   /** Cache of singleton objects: bean name to bean instance. */
    /**
     * 一級緩存  這個就是我們大名鼎鼎的緩存池, 用於保存我們所有的實例bean
     */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    /** Cache of singleton factories: bean name to ObjectFactory. */
    /**
     * 三級緩存  該map用戶緩存key為beanName, value為objectFactory(包裝為早期對象)
     */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    /** Cache of early singleton objects: bean name to bean instance. */
    /**
     * 二級緩存, 用戶緩存我們的key為beanName, value是我們的早期對象(此時對象屬性還沒有...)
     */
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

 

細細想來, 這三個緩存都非常有存在的必要.

1.1 引入一級緩存

剛開始, 只有一級緩存, 在整個bean創建完成以后, 將其完整的bean放入到一級緩存中. 這樣有什么問題? 

1. bean創建一共有三大步驟, (實例化, 屬性賦值, 初始化) 等到整個過程都創建完, 在存入一級緩存, 多線程怎么辦? 第一個線程創建bean的過程中, 又來了一個線程, 他發現一級緩存這時候還沒有, 就回去再次創建. 那不就重復了么? ioc要求, bean是單例的.

2. 加鎖, 加鎖能否解決這個問題? 能, 但是效率超級低. 對一級緩存加鎖, 那么所有的對象創建過程中都要等待. 哪怕人家已經創建成功過. 效率太低, 不能接受

3. 於是就引入了二級緩存. 

1.2 引入二級緩存

二級緩存的引入, 可以解決一級緩存創建bean鏈路過長的問題,他在bean一旦被創建,立刻就放入到二級緩存. 整個bean創建完成以后, 在放入到一級緩存,刪除二級緩存. 這樣做可以解決多線程創建bean的問題. 縮短了整個鏈路. 同時, 每次從緩存中先獲取bean, 如果一級緩存中已經有了,那么直接返回. 不用在執行后面的創建代碼

那么,二級緩存有什么問題呢?

這就還需要知道一個問題, 那就是動態代理創建bean. 什么時候, 去使用動態代理創建bean? 通常我們說在初始化之后, 調用bean的后置處理器創建bean. 這只是大多數bean創建動態代理的時候. 那如果有循環依賴呢? 有循環依賴, 還在初始化之后創建就晚了. 這是需要在實例化之后創建. 這樣,動態代理的代碼就和創建bean耦合在一塊了. 違背單一性原則.

於是, 引入了三級緩存

1.3 引入三級緩存

三級緩存的引入是為了解決耦合問題. 讓每一個方法只做一件事. 巧妙的使用了接口函數. 

 這個接口函數什么用呢? 就相當於js中的回調函數. 我在前面定義好, 但是不執行. 直到滿足條件了, 才執行. 這個方法, 可以大范圍應用到實踐工作中.

比如: 調用動態代理創建bean. 剛開始實例化完成以后, 我就賦予你這個能力, 你可以調用動態代理. 但是, 到后面, 你是否真的能夠運用這個能力呢? 不一定, 只有滿足條件, 才會運用這個能力. 

二. 定義接口函數, 也叫鈎子函數

在循環依賴源碼中, 兩次使用到接口函數的方式. 

第一個是創建bean的時候. 第二個是三級緩存

下面來看看源碼,

第一次: 創建bean的時候, 定義了一個鈎子函數createBean()

sharedInstance = getSingleton(beanName, () -> { try { // 這里定義了一個鈎子函數. 此時只是定義, 並不執行. 在真正需要創建bean的地方才會執行
        return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean.
 destroySingleton(beanName); throw ex; }
});

實際上調用的時機是: 在getSingleton方法里面. 回調接口函數.

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "Bean name must not be null");
        synchronized (this.singletonObjects) {
            // 第一步: 從一級緩存中獲取單例對象
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                if (this.singletonsCurrentlyInDestruction) {
                    throw new BeanCreationNotAllowedException(beanName,
                            "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                            "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
                }
                // 第二步: 將bean添加到singletonsCurrentlyInCreation中, 表示bean正在創建
                beforeSingletonCreation(beanName);
                boolean newSingleton = false;
                boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = new LinkedHashSet<>();
                }
                try {
                    // 第三步: 這里調用getObject()鈎子方法, 就會回調匿名函數, 調用singletonFactory的createBean() singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                }
                catch (IllegalStateException ex) {
                    // Has the singleton object implicitly appeared in the meantime ->
                    // if yes, proceed with it since the exception indicates that state.
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        throw ex;
                    }
                }
                catch (BeanCreationException ex) {
                    if (recordSuppressedExceptions) {
                        for (Exception suppressedException : this.suppressedExceptions) {
                            ex.addRelatedCause(suppressedException);
                        }
                    }
                    throw ex;
                }
                finally {
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = null;
                    }
                    afterSingletonCreation(beanName);
                }
                if (newSingleton) {
                    addSingleton(beanName, singletonObject);
                }
            }
            return singletonObject;
        }
    }

 

第二次調用: 是在三級緩存定義的時候

調用addSingletonFactory(...)定義了一個鈎子函數. 這里僅僅是定義, 並不執行

// 把我們的早期對象包裝成一個singletonFactory對象, 該對象提供了getObject()方法, 把靜態的bean放到三級緩存中去了.
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

然后進入到addSingletonFactory內部, 只是把singletonFactory放入到了三級緩存中, 這里只是定義, 也並沒有執行

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);

                // 添加到已經注冊的singleton實例.
                this.registeredSingletons.add(beanName);
            }
        }
    }

什么時候執行的呢? 再從緩存中獲取對象的時候. 

@Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 從一級緩存中獲取bean實例對象
        Object singletonObject = this.singletonObjects.get(beanName);
        /**
         * 如果在第一級的緩存中沒有獲取到對象, 並且singletonsCurrentlyIncreation為true,也就是這個類正在創建.
         * 標明當前是一個循環依賴.
         *
         * 這里有處理循環依賴的問題.-- 我們使用三級緩存解決循環依賴
         */
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                /**
                 * 從二級緩存中拿bean, 二級緩存中的對象是一個早期對象
                 * 什么是早期對象?就是bean剛剛調用了構造方法, 還沒有給bean的屬性進行賦值, 和初始化, 這就是早期對象
                  */

                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    /**
                     * 從三級緩存拿bean, singletonFactories是用來解決循環依賴的關鍵所在.
                     * 在ios后期的過程中, 當bean調用了構造方法的時候, 把早期對象包裝成一個ObjectFactory對象,暴露在三級緩存中
                      */ ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) {
                        /** * 在這里通過暴露的ObjectFactory包裝對象. 通過調用他的getObject()方法來獲取對象 * 在這個環節中會調用getEarlyBeanReference()來進行后置處理 */ singletonObject = singletonFactory.getObject(); // 把早期對象放置在二級緩存中
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        // 刪除三級緩存
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

在這里調用三級緩存, singletonObject = singletonFactory.getObject(); 回調鈎子函數. 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM