1. 程式人生 > >spring深入學習(十五) IOC 之從單例快取中獲取單例 bean

spring深入學習(十五) IOC 之從單例快取中獲取單例 bean

從這篇部落格開始我們開始載入 bean 的第一個步驟,從快取中獲取 bean,程式碼片段如下:

        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            if (logger.isDebugEnabled()) {
                if (isSingletonCurrentlyInCreation(beanName)) {
                    logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                            "' that is not fully initialized yet - a consequence of a circular reference");
                }
                else {
                    logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
                }
            }
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }

首先呼叫 getSingleton() 從快取中獲取 bean,在上篇部落格 【死磕 Spring】—– 載入 bean 之 開啟 bean 的載入 提到過,Spring 對單例模式的 bean 只會建立一次,後續如果再獲取該 bean 則是直接從單例快取中獲取,該過程就體現在 getSingleton() 中。如下:

    public Object getSingleton(String beanName) {
        return getSingleton(beanName, true);
    }

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 從單例緩衝中載入 bean
        Object singletonObject = this.singletonObjects.get(beanName);

        // 快取中的 bean 為空,且當前 bean 正在建立
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            // 加鎖
            synchronized (this.singletonObjects) {
                // 從 earlySingletonObjects 獲取
                singletonObject = this.earlySingletonObjects.get(beanName);
                // earlySingletonObjects 中沒有,且允許提前建立
                if (singletonObject == null && allowEarlyReference) {
                    // 從 singletonFactories 中獲取對應的 ObjectFactory
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    // ObjectFactory 不為空,則建立 bean
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

這段程式碼非常簡單,首先從 singletonObjects 中獲取,若為空且當前 bean 正在建立中,則從 earlySingletonObjects 中獲取,若為空且允許提前建立則從 singletonFactories 中獲取相應的 ObjectFactory ,若不為空,則呼叫其 getObject() 建立 bean,然後將其加入到 earlySingletonObjects,然後從 singletonFactories 刪除。總體邏輯就是根據 beanName 依次檢測這三個 Map,若為空,從下一個,否則返回。這三個 Map 存放的都有各自的功能,如下:

  • singletonObjects :存放的是單例 bean,對應關係為 bean name --> bean instance
  • earlySingletonObjects:存放的是早期的 bean,對應關係也是 bean name --> bean instance。它與 singletonObjects 區別在於 earlySingletonObjects 中存放的 bean 不一定是完整的,從上面過程中我們可以瞭解,bean 在建立過程中就已經加入到 earlySingletonObjects 中了,所以當在 bean 的建立過程中就可以通過 getBean() 方法獲取。這個 Map 也是解決迴圈依賴的關鍵所在。
  • singletonFactories:存放的是 ObjectFactory,可以理解為建立單例 bean 的 factory,對應關係是 bean name --> ObjectFactory

在上面程式碼中還有一個非常重要的檢測方法 isSingletonCurrentlyInCreation(beanName),該方法用於判斷該 beanName 對應的 bean 是否在建立過程中,注意這個過程講的是整個工廠中。如下:

    public boolean isSingletonCurrentlyInCreation(String beanName) {
        return this.singletonsCurrentlyInCreation.contains(beanName);
    }

從這段程式碼中我們可以預測,在 bean 建立過程中都會將其加入到 singletonsCurrentlyInCreation 集合中,具體是在什麼時候加的,我們後面分析。

到這裡從快取中獲取 bean 的過程已經分析完畢了,我們再看開篇的程式碼段,從快取中獲取 bean 後,若其不為 null 且 args 為空,則會呼叫 getObjectForBeanInstance() 處理。為什麼會有這麼一段呢?因為我們從快取中獲取的 bean 是最原始的 bean 並不一定使我們最終想要的 bean,怎麼辦呢?呼叫 getObjectForBeanInstance() 進行處理,該方法的定義為獲取給定 bean 例項的物件,該物件要麼是 bean 例項本身,要麼就是 FactoryBean 建立的物件,如下:

   protected Object getObjectForBeanInstance(
            Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

        // 若為工廠類引用(name 以 & 開頭)
        if (BeanFactoryUtils.isFactoryDereference(name)) {
            // 如果是 NullBean,則直接返回
            if (beanInstance instanceof NullBean) {
                return beanInstance;
            }
            // 如果 beanInstance 不是 FactoryBean 型別,則丟擲異常
            if (!(beanInstance instanceof FactoryBean)) {
                throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
            }
        }

        // 到這裡我們就有了一個 bean 例項,當然該例項可能是會是是一個正常的 bean 又或者是一個 FactoryBean
        // 如果是 FactoryBean,我我們則建立該 bean
        if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
            return beanInstance;
        }

        // 載入 FactoryBean
        Object object = null;
        // 若 BeanDefinition 為 null,則從快取中載入
        if (mbd == null) {
            object = getCachedObjectForFactoryBean(beanName);
        }
        // 若 object 依然為空,則可以確認,beanInstance 一定是 FactoryBean
        if (object == null) {
            FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
            //
            if (mbd == null && containsBeanDefinition(beanName)) {
                mbd = getMergedLocalBeanDefinition(beanName);
            }
            // 是否是使用者定義的而不是應用程式本身定義的
            boolean synthetic = (mbd != null && mbd.isSynthetic());
            // 核心處理類
            object = getObjectFromFactoryBean(factory, beanName, !synthetic);
        }
        return object;
    }

該方法主要是進行檢測工作的,主要如下:

  • 若 name 為工廠相關的(以 & 開頭),且 beanInstance 為 NullBean 型別則直接返回,如果 beanInstance 不為 FactoryBean 型別則丟擲 BeanIsNotAFactoryException 異常。這裡主要是校驗 beanInstance 的正確性。
  • 如果 beanInstance 不為 FactoryBean 型別或者 name 也不是與工廠相關的,則直接返回。這裡主要是對非 FactoryBean 型別處理。
  • 如果 BeanDefinition 為空,則從 factoryBeanObjectCache 中載入,如果還是空,則可以斷定 beanInstance 一定是 FactoryBean 型別,則委託 getObjectFromFactoryBean() 方法處理

從上面可以看出 getObjectForBeanInstance() 主要是返回給定的 bean 例項物件,當然該例項物件為非 FactoryBean 型別,對於 FactoryBean 型別的 bean,則是委託 getObjectFromFactoryBean() 從 FactoryBean 獲取 bean 例項物件。

   protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
        // 為單例模式且快取中存在
        if (factory.isSingleton() && containsSingleton(beanName)) {

            synchronized (getSingletonMutex()) {
                // 從快取中獲取指定的 factoryBean
                Object object = this.factoryBeanObjectCache.get(beanName);

                if (object == null) {
                    // 為空,則從 FactoryBean 中獲取物件
                    object = doGetObjectFromFactoryBean(factory, beanName);

                    // 從快取中獲取
                    Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                    // **我實在是不明白這裡這麼做的原因,這裡是幹嘛???**
                    if (alreadyThere != null) {
                        object = alreadyThere;
                    }
                    else {
                        // 需要後續處理
                        if (shouldPostProcess) {
                            // 若該 bean 處於建立中,則返回非處理物件,而不是儲存它
                            if (isSingletonCurrentlyInCreation(beanName)) {
                                return object;
                            }
                            // 前置處理
                            beforeSingletonCreation(beanName);
                            try {
                                // 對從 FactoryBean 獲取的物件進行後處理
                                // 生成的物件將暴露給bean引用
                                object = postProcessObjectFromFactoryBean(object, beanName);
                            }
                            catch (Throwable ex) {
                                throw new BeanCreationException(beanName,
                                        "Post-processing of FactoryBean's singleton object failed", ex);
                            }
                            finally {
                                // 後置處理
                                afterSingletonCreation(beanName);
                            }
                        }
                        // 快取
                        if (containsSingleton(beanName)) {
                            this.factoryBeanObjectCache.put(beanName, object);
                        }
                    }
                }
                return object;
            }
        }
        else {
            // 非單例
            Object object = doGetObjectFromFactoryBean(factory, beanName);
            if (shouldPostProcess) {
                try {
                    object = postProcessObjectFromFactoryBean(object, beanName);
                }
                catch (Throwable ex) {
                    throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
                }
            }
            return object;
        }
    }

主要流程如下:

  • 若為單例且單例 bean 快取中存在 beanName,則進行後續處理(跳轉到下一步),否則則從 FactoryBean 中獲取 bean 例項物件,如果接受後置處理,則呼叫 postProcessObjectFromFactoryBean() 進行後置處理。
  • 首先獲取鎖(其實我們在前面篇幅中發現了大量的同步鎖,鎖住的物件都是 this.singletonObjects, 主要是因為在單例模式中必須要保證全域性唯一),然後從 factoryBeanObjectCache 快取中獲取例項物件 object,若 object 為空,則呼叫 doGetObjectFromFactoryBean() 方法從 FactoryBean 獲取物件,其實內部就是呼叫 FactoryBean.getObject()
  • 如果需要後續處理,則進行進一步處理,步驟如下:
    • 若該 bean 處於建立中(isSingletonCurrentlyInCreation),則返回非處理物件,而不是儲存它
    • 呼叫 beforeSingletonCreation() 進行建立之前的處理。預設實現將該 bean 標誌為當前建立的。
    • 呼叫 postProcessObjectFromFactoryBean() 對從 FactoryBean 獲取的 bean 例項物件進行後置處理,預設實現是按照原樣直接返回,具體實現是在 AbstractAutowireCapableBeanFactory 中實現的,當然子類也可以重寫它,比如應用後置處理
    • 呼叫 afterSingletonCreation() 進行建立 bean 之後的處理,預設實現是將該 bean 標記為不再在建立中。
  • 最後加入到 FactoryBeans 快取中。

該方法應該就是建立 bean 例項物件中的核心方法之一了。這裡我們關注三個方法:beforeSingletonCreation() 、 afterSingletonCreation() 、 postProcessObjectFromFactoryBean()。可能有小夥伴覺得前面兩個方法不是很重要,LZ 可以肯定告訴你,這兩方法是非常重要的操作,因為他們記錄著 bean 的載入狀態,是檢測當前 bean 是否處於建立中的關鍵之處,對解決 bean 迴圈依賴起著關鍵作用。before 方法用於標誌當前 bean 處於建立中,after 則是移除。其實在這篇部落格剛剛開始就已經提到了 isSingletonCurrentlyInCreation() 是用於檢測當前 bean 是否處於建立之中,如下:

    public boolean isSingletonCurrentlyInCreation(String beanName) {
        return this.singletonsCurrentlyInCreation.contains(beanName);
    }

是根據 singletonsCurrentlyInCreation 集合中是否包含了 beanName,集合的元素則一定是在 beforeSingletonCreation() 中新增的,如下:

    protected void beforeSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    }

afterSingletonCreation() 為移除,則一定就是對 singletonsCurrentlyInCreation 集合 remove 了,如下:

    protected void afterSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
            throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
        }
    }

postProcessObjectFromFactoryBean() 是對從 FactoryBean 處獲取的 bean 例項物件進行後置處理,其預設實現是直接返回 object 物件,不做任何處理,子類可以重寫,例如應用後處理器。AbstractAutowireCapableBeanFactory 對其提供了實現,如下:

    protected Object postProcessObjectFromFactoryBean(Object object, String beanName) {
        return applyBeanPostProcessorsAfterInitialization(object, beanName);
    }

該方法的定義為:對所有的 {@code postProcessAfterInitialization} 進行回撥註冊 BeanPostProcessors,讓他們能夠後期處理從 FactoryBean 中獲取的物件。下面是具體實現:

    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

對於後置處理器,這裡我們不做過多闡述,後面會專門的博文進行詳細介紹,這裡我們只需要記住一點:儘可能保證所有 bean 初始化後都會呼叫註冊的 BeanPostProcessor.postProcessAfterInitialization() 方法進行處理,在實際開發過程中大可以針對此特性設計自己的業務邏輯。

至此,從快取中獲取 bean 物件過程已經分析完畢了。

下面兩篇部落格分析,如果從單例快取中沒有獲取到單例 bean,則 Spring 是如何處理的?