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

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

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

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

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

如今總結循環依賴的思想多線程

學了那麼多, 爲何說見多才能識廣呢 , 知作別人是如何解決某一類問題的, 也就是優秀代碼的魅力. 這也是爲何要學習別人的代碼的緣由.app

思想纔是咱們能夠在工做中借鑑使用的

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

2. 接口函數源碼分析

 


 

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

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

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(); 回調鉤子函數. 

相關文章
相關標籤/搜索