歡迎光臨
每天分享高質量文章

【死磕 Spring】—– IOC 之從單例快取中獲取單例 bean

精品專欄

 

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

  1.        Object sharedInstance = getSingleton(beanName);

  2.        if (sharedInstance != null && args == null) {

  3.            if (logger.isDebugEnabled()) {

  4.                if (isSingletonCurrentlyInCreation(beanName)) {

  5.                    logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +

  6.                            "' that is not fully initialized yet - a consequence of a circular reference");

  7.                }

  8.                else {

  9.                    logger.debug("Returning cached instance of singleton bean '" + beanName + "'");

  10.                }

  11.            }

  12.            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);

  13.        }

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

  1.    public Object getSingleton(String beanName) {

  2.        return getSingleton(beanName, true);

  3.    }

  4.    protected Object getSingleton(String beanName, boolean allowEarlyReference) {

  5.        // 從單例緩衝中載入 bean

  6.        Object singletonObject = this.singletonObjects.get(beanName);

  7.        // 快取中的 bean 為空,且當前 bean 正在建立

  8.        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {

  9.            // 加鎖

  10.            synchronized (this.singletonObjects) {

  11.                // 從 earlySingletonObjects 獲取

  12.                singletonObject = this.earlySingletonObjects.get(beanName);

  13.                // earlySingletonObjects 中沒有,且允許提前建立

  14.                if (singletonObject == null && allowEarlyReference) {

  15.                    // 從 singletonFactories 中獲取對應的 ObjectFactory

  16.                    ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);

  17.                    // ObjectFactory 不為空,則建立 bean

  18.                    if (singletonFactory != null) {

  19.                        singletonObject = singletonFactory.getObject();

  20.                        this.earlySingletonObjects.put(beanName, singletonObject);

  21.                        this.singletonFactories.remove(beanName);

  22.                    }

  23.                }

  24.            }

  25.        }

  26.        return singletonObject;

  27.    }

這段程式碼非常簡單,首先從 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 是否在建立過程中,註意這個過程講的是整個工廠中。如下:

  1.    public boolean isSingletonCurrentlyInCreation(String beanName) {

  2.        return this.singletonsCurrentlyInCreation.contains(beanName);

  3.    }

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

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

  1.   protected Object getObjectForBeanInstance(

  2.            Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

  3.        // 若為工廠類取用(name 以 & 開頭)

  4.        if (BeanFactoryUtils.isFactoryDereference(name)) {

  5.            // 如果是 NullBean,則直接傳回

  6.            if (beanInstance instanceof NullBean) {

  7.                return beanInstance;

  8.            }

  9.            // 如果 beanInstance 不是 FactoryBean 型別,則丟擲異常

  10.            if (!(beanInstance instanceof FactoryBean)) {

  11.                throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());

  12.            }

  13.        }

  14.        // 到這裡我們就有了一個 bean 實體,當然該實體可能是會是是一個正常的 bean 又或者是一個 FactoryBean

  15.        // 如果是 FactoryBean,我我們則建立該 bean

  16.        if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {

  17.            return beanInstance;

  18.        }

  19.        // 載入 FactoryBean

  20.        Object object = null;

  21.        // 若 BeanDefinition 為 null,則從快取中載入

  22.        if (mbd == null) {

  23.            object = getCachedObjectForFactoryBean(beanName);

  24.        }

  25.        // 若 object 依然為空,則可以確認,beanInstance 一定是 FactoryBean

  26.        if (object == null) {

  27.            FactoryBean> factory = (FactoryBean>) beanInstance;

  28.            //

  29.            if (mbd == null && containsBeanDefinition(beanName)) {

  30.                mbd = getMergedLocalBeanDefinition(beanName);

  31.            }

  32.            // 是否是使用者定義的而不是應用程式本身定義的

  33.            boolean synthetic = (mbd != null && mbd.isSynthetic());

  34.            // 核心處理類

  35.            object = getObjectFromFactoryBean(factory, beanName, !synthetic);

  36.        }

  37.        return object;

  38.    }

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

  • 若 name 為工廠相關的(以 & 開頭),且 beanInstance 為 NullBean 型別則直接傳回,如果 beanInstance 不為 FactoryBean 型別則丟擲 BeanIsNotAFactoryException 異常。這裡主要是校驗 beanInstance 的正確性。

  • 如果 beanInstance 不為 FactoryBean 型別或者 name 也不是與工廠相關的,則直接傳回。這裡主要是對非 FactoryBean 型別處理。

  • 如果 BeanDefinition 為空,則從 factoryBeanObjectCache 中載入,如果還是空,則可以斷定 beanInstance 一定是 FactoryBean 型別,則委託 getObjectFromFactoryBean() 方法處理

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

  1.   protected Object getObjectFromFactoryBean(FactoryBean> factory, String beanName, boolean shouldPostProcess) {

  2.        // 為單例樣式且快取中存在

  3.        if (factory.isSingleton() && containsSingleton(beanName)) {

  4.            synchronized (getSingletonMutex()) {

  5.                // 從快取中獲取指定的 factoryBean

  6.                Object object = this.factoryBeanObjectCache.get(beanName);

  7.                if (object == null) {

  8.                    // 為空,則從 FactoryBean 中獲取物件

  9.                    object = doGetObjectFromFactoryBean(factory, beanName);

  10.                    // 從快取中獲取

  11.                    Object alreadyThere = this.factoryBeanObjectCache.get(beanName);

  12.                    // **我實在是不明白這裡這麼做的原因,這裡是幹嘛???**

  13.                    if (alreadyThere != null) {

  14.                        object = alreadyThere;

  15.                    }

  16.                    else {

  17.                        // 需要後續處理

  18.                        if (shouldPostProcess) {

  19.                            // 若該 bean 處於建立中,則傳回非處理物件,而不是儲存它

  20.                            if (isSingletonCurrentlyInCreation(beanName)) {

  21.                                return object;

  22.                            }

  23.                            // 前置處理

  24.                            beforeSingletonCreation(beanName);

  25.                            try {

  26.                                // 對從 FactoryBean 獲取的物件進行後處理

  27.                                // 生成的物件將暴露給bean取用

  28.                                object = postProcessObjectFromFactoryBean(object, beanName);

  29.                            }

  30.                            catch (Throwable ex) {

  31.                                throw new BeanCreationException(beanName,

  32.                                        "Post-processing of FactoryBean's singleton object failed", ex);

  33.                            }

  34.                            finally {

  35.                                // 後置處理

  36.                                afterSingletonCreation(beanName);

  37.                            }

  38.                        }

  39.                        // 快取

  40.                        if (containsSingleton(beanName)) {

  41.                            this.factoryBeanObjectCache.put(beanName, object);

  42.                        }

  43.                    }

  44.                }

  45.                return object;

  46.            }

  47.        }

  48.        else {

  49.            // 非單例

  50.            Object object = doGetObjectFromFactoryBean(factory, beanName);

  51.            if (shouldPostProcess) {

  52.                try {

  53.                    object = postProcessObjectFromFactoryBean(object, beanName);

  54.                }

  55.                catch (Throwable ex) {

  56.                    throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);

  57.                }

  58.            }

  59.            return object;

  60.        }

  61.    }

主要流程如下:

  • 若為單例且單例 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 是否處於建立之中,如下:

  1.    public boolean isSingletonCurrentlyInCreation(String beanName) {

  2.        return this.singletonsCurrentlyInCreation.contains(beanName);

  3.    }

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

  1.    protected void beforeSingletonCreation(String beanName) {

  2.        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {

  3.            throw new BeanCurrentlyInCreationException(beanName);

  4.        }

  5.    }

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

  1.    protected void afterSingletonCreation(String beanName) {

  2.        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {

  3.            throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");

  4.        }

  5.    }

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

  1.    protected Object postProcessObjectFromFactoryBean(Object object, String beanName) {

  2.        return applyBeanPostProcessorsAfterInitialization(object, beanName);

  3.    }

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

  1.    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)

  2.            throws BeansException {

  3.        Object result = existingBean;

  4.        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {

  5.            Object current = beanProcessor.postProcessAfterInitialization(result, beanName);

  6.            if (current == null) {

  7.                return result;

  8.            }

  9.            result = current;

  10.        }

  11.        return result;

  12.    }

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

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

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

【死磕 Spring】----- IOC 之 IOC 初始化總結

【死磕 Spring】----- IOC 之解析 bean 標簽:解析自定義標簽

【死磕 Spring】----- IOC 之解析 bean 標簽:constructor-arg、property 子元素

【死磕 Spring】—– IOC 之解析 bean 標簽:meta、lookup-method、replace-method)

【死磕 Spring】----- IOC 之解析 bean 標簽:開啟解析行程

【死磕 Spring】----- IOC 之 獲取 Document 物件

【死磕 Spring】----- IOC 之 註冊 BeanDefinition

【死磕 Spring】----- IOC 之 獲取驗證模型

【死磕 Spring】----- IOC 之 Spring 統一資源載入策略

【死磕 Spring】----- IOC 之深入理解 Spring IoC

END


我是 Java 技術驛站,感謝有你


>>>>>> 加群交流技術 <<<<<<

贊(0)

分享創造快樂

© 2024 知識星球   網站地圖