Spring IOC源碼解析-獲取單例bean

一.簡介

假設當前IOC容器已經載入用戶定義的Bean信息,開始分析依賴注入的原理。首先,注意到依賴注入的過程是用戶第一次向IOC容器索要Bean時觸發的,當然也有例外,也就是我們可以在BeanDefinition信息中通過控制lazy-init屬性來讓容器完成對Bean的預實例化。這個預實例化過程實際上也是一個完成依賴出入的過程,但是它在初始化的過程中完成的(這個部分在這篇文章中就不詳細講述了)。當用戶向IOC索要Bean的時,在基本IOC容器接口BeanFactory中,有一個getBean的接口定義,這個接口的實現就是觸發依賴注入的地方。下面從DefaultListableBeanFactory的基類AbstractBeanFactory去看getBean的實現,但是並沒有關注其中調用的方法。

二.源碼分析

2.1 getBean(String)源碼

public Object getBean(String name) throws BeansException {
    // getBean 是一個空殼方法,所有的邏輯都封裝在 doGetBean 方法中
    return doGetBean(name, null, null, false);
}

protected <T> T doGetBean(
        final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
        throws BeansException {

    // 1.
    /*
     * 通過 name 獲取 beanName。這裏不使用 name 直接作爲 beanName 有兩點原因:
     * 1. name 可能會以 & 字符開頭,表明調用者想獲取 FactoryBean 本身,而非 FactoryBean 
     *    實現類所創建的 bean。在 BeanFactory 中,FactoryBean 的實現類和其他的 bean 存儲
     *    方式是一致的,即 <beanName, bean>,beanName 中是沒有 & 這個字符的。所以我們需要
     *    將 name 的首字符 & 移除,這樣才能從緩存裏取到 FactoryBean 實例。
     * 2. 若 name 是一個別名,則應將別名轉換爲具體的實例名,也就是 beanName。
     */
    final String beanName = transformedBeanName(name);
    Object bean;

   
    //2.
    /*
     * 從緩存中獲取單例 bean。Spring 是使用 Map 作爲 beanName 和 bean 實例的緩存的,所以這
     * 裏暫時可以把 getSingleton(beanName) 等價於 beanMap.get(beanName)。當然,實際的
     * 邏輯並非如此簡單,後面再細說。
     */
    Object sharedInstance = getSingleton(beanName);

    /*
     * 如果 sharedInstance = null,則說明緩存裏沒有對應的實例,表明這個實例還沒創建。
     * BeanFactory 並不會在一開始就將所有的單例 bean 實例化好,而是在調用 getBean 獲取 
     * bean 時再實例化,也就是懶加載。
     * getBean 方法有很多重載,比如 getBean(String name, Object... args),我們在首次獲取
     * 某個 bean 時,可以傳入用於初始化 bean 的參數數組(args),BeanFactory 會根據這些參數
     * 去匹配合適的構造方法構造 bean 實例。當然,如果單例 bean 早已創建好,這裏的 args 就沒有
     * 用了,BeanFactory 不會多次實例化單例 bean。
     */
    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 + "'");
            }
        }
      
       
     //3.
        /*
         * 如果 sharedInstance 是普通的單例 bean,下面的方法會直接返回。但如果 
         * sharedInstance 是 FactoryBean 類型的,則需調用 getObject 工廠方法獲取真正的 
         * bean 實例。如果用戶想獲取 FactoryBean 本身,這裏也不會做特別的處理,直接返回
         * 即可。畢竟 FactoryBean 的實現類本身也是一種 bean,只不過具有一點特殊的功能而已。
         */
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
   //4.
    /*
     * 如果上面的條件不滿足,則表明 sharedInstance 可能爲空,此時 beanName 對應的 bean 
     * 實例可能還未創建。這裏還存在另一種可能,如果當前容器有父容器,beanName 對應的 bean 實例
     * 可能是在父容器中被創建了,所以在創建實例前,需要先去父容器裏檢查一下。
     */
    else {
        // BeanFactory 不緩存 Prototype 類型的 bean,無法處理該類型 bean 的循環依賴問題
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        //5.
        // 如果 sharedInstance = null,則到父容器中查找 bean 實例
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            // 獲取 name 對應的 beanName,如果 name 是以 & 字符開頭,則返回 & + beanName
            String nameToLookup = originalBeanName(name);
            // 根據 args 是否爲空,以決定調用父容器哪個方法獲取 bean
            if (args != null) {
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            } 
            else {
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
        }

        if (!typeCheckOnly) {
            markBeanAsCreated(beanName);
        }

        try {
             //6.
            // 合併父 BeanDefinition 與子 BeanDefinition,後面會單獨分析這個方法
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            checkMergedBeanDefinition(mbd, beanName, args);
       
  
             //7.
            // 檢查是否有 dependsOn 依賴,如果有則先初始化所依賴的 bean
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                    /*
                     * 檢測是否存在 depends-on 循環依賴,若存在則拋異常。比如 A 依賴 B,
                     * B 又依賴 A,他們的配置如下:
                     *   <bean id="beanA" class="BeanA" depends-on="beanB">
                     *   <bean id="beanB" class="BeanB" depends-on="beanA">
                     *   
                     * beanA 要求 beanB 在其之前被創建,但 beanB 又要求 beanA 先於它
                     * 創建。這個時候形成了循環,對於 depends-on 循環,Spring 會直接
                     * 拋出異常
                     */
                    if (isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }
                    // 註冊依賴記錄
                    registerDependentBean(dep, beanName);
                    try {
                        // 加載 depends-on 依賴
                        getBean(dep);
                    } 
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                    }
                }
            }
            //8.
            // 創建 bean 實例
            if (mbd.isSingleton()) {
                /*
                 * 這裏並沒有直接調用 createBean 方法創建 bean 實例,而是通過 
                 * getSingleton(String, ObjectFactory) 方法獲取 bean 實例。
                 * getSingleton(String, ObjectFactory) 方法會在內部調用 
                 * ObjectFactory 的 getObject() 方法創建 bean,並會在創建完成後,
                 * 將 bean 放入緩存中。關於 getSingleton 方法的分析,本文先不展開,我會在
                 * 後面的文章中進行分析
                 */
                sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                    @Override
                    public Object getObject() throws BeansException {
                        try {
                            // 創建 bean 實例
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            destroySingleton(beanName);
                            throw ex;
                        }
                    }
                });
                // 如果 bean 是 FactoryBean 類型,則調用工廠方法獲取真正的 bean 實例。否則直接返回 bean 實例
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }

            // 創建 prototype 類型的 bean 實例
            else if (mbd.isPrototype()) {
                Object prototypeInstance = null;
                try {
                    beforePrototypeCreation(beanName);
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    afterPrototypeCreation(beanName);
                }
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }

            // 創建其他類型的 bean 實例
            else {
                String scopeName = mbd.getScope();
                final Scope scope = this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                    Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
                            beforePrototypeCreation(beanName);
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            finally {
                                afterPrototypeCreation(beanName);
                            }
                        }
                    });
                    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new BeanCreationException(beanName,
                            "Scope '" + scopeName + "' is not active for the current thread; consider " +
                            "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                            ex);
                }
            }
        }
        catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
    }

    //9.
    // 如果需要進行類型轉換,則在此處進行轉換。類型轉換這一塊我沒細看,就不多說了。
    if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
        try {
            return getTypeConverter().convertIfNecessary(bean, requiredType);
        }
        catch (TypeMismatchException ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Failed to convert bean '" + name + "' to required type '" +
                        ClassUtils.getQualifiedName(requiredType) + "'", ex);
            }
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    }

    // 返回 bean
    return (T) bean;
}

doGetBean的工作流程如下:

1.轉換對應的beanName。

2.嘗試從緩存中加載單例。單例在Spring的同一個容器中只會被創建一次,後續在獲取單例,就直接從單例緩存中獲取。當然這裏的也只是嘗試加載,首先嚐試從緩存中加載,如果加載不成功則再次從singletonFactories中加載。因爲在創建單例bean的時候會存在依賴注入的情況,而在創建依賴的時候爲了避免循環依賴,在Spring創建bean的原則是不等bean創建完成後就會將創建bean的ObjectFactory提前曝光加入到緩存中,一旦下一個bean創建的時候需要依賴上一個bean則直接使用ObjectFactory(後面會詳細講解循環依賴)。

3.bean的實例化。如果從緩存中得到了bean的原始狀態,則需要對bean進行實例化。緩存中記錄的只是最原始的bean的狀態,並不一定使我們最終想要的bean。舉個例子,假如我們需要對工廠bean進行處理,那麼這裏得到的其實就是工廠bean的初始狀態,但實際需要的是工廠bean中定義的factory-method方法中返回的bean,而getObjectForBeanInstance就是完成這個工作的,後面會詳細講解。

4.原型模式的依賴檢查。只有單例情況下才會嘗試解決循環依賴,如果存在A中有B的屬性,B中有A的屬性,那麼當依賴注入的時候,就會產生當A還未創建完的時候因爲對B的創建再次返回創建A,造成循環依賴,也就是情況:isPrototypeCurrentInCreation(beanName)判斷爲true。

5.檢測parentBeanFactory。

6.將存儲XML配置文件的GernericBeanDefinition轉換爲RootBeanDefinition。因爲從XML配置文件讀取的Bean信息存儲在GernericBeanDefinition,但是所有Bean後續處理都是針對於RootBeanDefinition的,所以這裏需要進行一個轉換,轉換的同時,如果父類bean不爲空的話,則會一併合併父類的屬性。

7.尋找依賴。

8.針對不同的scope進行bean的創建。

9.類型轉換。

以上步驟對應的流程圖如下:

2.2 beanName轉換

在獲取bean實例之前,String第一件要做的事情就是對參數name進行轉換。轉換的目的主要是爲了解決兩個問題,第一個問題是處理字符&開頭的name,防止BeanFactory無法找到與name對應的bean實例。第二個是處理別名問題,Spring不會存儲<別名,bean實例>這種映射,僅會存儲<beanName,bean>。所以,同樣是爲了避免BeanFactory找不到name對應的bean的實例,對於別名也要進行轉換。下來,對轉換過程進行分析,如下:

protected String transformedBeanName(String name) {
    // 這裏調用了兩個方法:BeanFactoryUtils.transformedBeanName(name) 和 canonicalName
    return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

/** 該方法用於處理 & 字符 */
public static String transformedBeanName(String name) {
    Assert.notNull(name, "'name' must not be null");
    String beanName = name;
    // 循環處理 & 字符。比如 name = "&&&&&helloService",最終會被轉成 helloService
    while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
        beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
    }
    return beanName;
}

/** 該方法用於轉換別名 */
public String canonicalName(String name) {
    String canonicalName = name;
    String resolvedName;
    /*
     * 這裏使用 while 循環進行處理,原因是:可能會存在多重別名的問題,即別名指向別名。比如下面
     * 的配置:
     *   <bean id="hello" class="service.Hello"/>
     *   <alias name="hello" alias="aliasA"/>
     *   <alias name="aliasA" alias="aliasB"/>
     *
     * 上面的別名指向關係爲 aliasB -> aliasA -> hello,對於上面的別名配置,aliasMap 中數據
     * 視圖爲:aliasMap = [<aliasB, aliasA>, <aliasA, hello>]。通過下面的循環解析別名
     * aliasB 最終指向的 beanName
     */
    do {
        resolvedName = this.aliasMap.get(canonicalName);
        if (resolvedName != null) {
                canonicalName = resolvedName;
        }
    }
    while (resolvedName != null);
    return canonicalName;
}

2.3 從緩存中獲取bean實例

對於單例bean,Spring容器只會實例化一次。後面再次獲取時,只需要從緩存裏獲取即可,無需且不能再次實例化。從緩存取bean實例的方法是getSingleton(String),下來對這個方法進行分析:

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

/**
 * 這裏解釋一下 allowEarlyReference 參數,allowEarlyReference 表示是否允許其他 bean 引用
 * 正在創建中的 bean,用於處理循環引用的問題。關於循環引用,這裏先簡單介紹一下。先看下面的配置:
 *
 *   <bean id="hello" class="xyz.coolblog.service.Hello">
 *       <property name="world" ref="world"/>
 *   </bean>
 *   <bean id="world" class="xyz.coolblog.service.World">
 *       <property name="hello" ref="hello"/>
 *   </bean>
 * 
 * 如上所示,hello 依賴 world,world 又依賴於 hello,他們之間形成了循環依賴。Spring 在構建 
 * hello 這個 bean 時,會檢測到它依賴於 world,於是先去實例化 world。實例化 world 時,發現 
 * world 依賴 hello。這個時候容器又要去初始化 hello。由於 hello 已經在初始化進程中了,爲了讓 
 * world 能完成初始化,這裏先讓 world 引用正在初始化中的 hello。world 初始化完成後,hello 
 * 就可引用到 world 實例,這樣 hello 也就能完成初始了。關於循環依賴,我後面會專門寫一篇文章講
 * 解,這裏先說這麼多。
 */
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 從 singletonObjects 獲取實例,singletonObjects 中緩存的實例都是完全實例化好的 bean,可以直接使用
    Object singletonObject = this.singletonObjects.get(beanName);
    /*
     * 如果 singletonObject = null,表明還沒創建,或者還沒完全創建好。
     * 這裏判斷 beanName 對應的 bean 是否正在創建中
     */
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 從 earlySingletonObjects 中獲取提前曝光的 bean,用於處理循環引用
            singletonObject = this.earlySingletonObjects.get(beanName);
            // 如果如果 singletonObject = null,且允許提前曝光 bean 實例,則從相應的 ObjectFactory 獲取一個原始的(raw)bean(尚未填充屬性)
            if (singletonObject == null && allowEarlyReference) {
                // 獲取相應的工廠類
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 提前曝光 bean 實例,用於解決循環依賴
                    singletonObject = singletonFactory.getObject();
                    // 放入緩存中,如果還有其他 bean 依賴當前 bean,其他 bean 可以直接從 earlySingletonObjects 取結果
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

這個方法首先嚐試從singletonObjects裏面獲取實例,如果獲取不到再從earlySingletonObjects裏面獲取,如果還獲取不到,再嘗試從singletonFactories裏面獲取beanName對應的ObjectFactory,然後調用這個ObjectFactory的getObject來創建bean,並放到earlySingletonObjects裏面去,並且從singletonFactories裏面remove掉這個ObjectFactory,對於後續的所有內存操作都只是爲了循環依賴檢測時候使用,也就是allowEarlyReference爲true的情況下使用。(這個後面解決循環依賴的時候會講到)

這裏涉及用於存儲bean的不同的map,簡單解釋下:

  • singletonObjects:用於存放完全初始化好的bean,從該緩存中取出的bean可以直接使用。
  • earlySingletonObjects:用於存放還在初始化的bean,用於解決循環依賴
  • singletonFactories:用於存放bean工廠。bean工廠所產生的bean時還未初始化的bean,如代碼所示,bean工廠所生成的對象最終會被緩存到earlySingletonObjects中。

2.4 合併父BeanDefinition和子BeanDefinition

我們知道,Spring支持配置繼承,在標籤中可以使用parent屬性配置父類bean。這樣子類bean可以繼承父類bean的配置信息,同時也可以覆蓋父類中的配置。

源碼分析如下:

protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
    // 檢查緩存中是否存在“已合併的 BeanDefinition”,若有直接返回即可
    RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
    if (mbd != null) {
        return mbd;
    }
    // 調用重載方法
    return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}

protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
        throws BeanDefinitionStoreException {
    // 繼續調用重載方法
    return getMergedBeanDefinition(beanName, bd, null);
}

protected RootBeanDefinition getMergedBeanDefinition(
        String beanName, BeanDefinition bd, BeanDefinition containingBd)
        throws BeanDefinitionStoreException {

    synchronized (this.mergedBeanDefinitions) {
        RootBeanDefinition mbd = null;

        if (containingBd == null) {
            mbd = this.mergedBeanDefinitions.get(beanName);
        }

        if (mbd == null) {
            // bd.getParentName() == null,表明無父配置,這時直接將當前的 BeanDefinition 升級爲 RootBeanDefinition
            if (bd.getParentName() == null) {
                if (bd instanceof RootBeanDefinition) {
                    mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
                }
                else {
                    mbd = new RootBeanDefinition(bd);
                }
            }
            else {
                BeanDefinition pbd;
                try {
                    String parentBeanName = transformedBeanName(bd.getParentName());
                    /*
                     * 判斷父類 beanName 與子類 beanName 名稱是否相同。若相同,則父類 bean 一定
                     * 在父容器中。原因也很簡單,容器底層是用 Map 緩存 <beanName, bean> 鍵值對
                     * 的。同一個容器下,使用同一個 beanName 映射兩個 bean 實例顯然是不合適的。
                     * 有的朋友可能會覺得可以這樣存儲:<beanName, [bean1, bean2]> ,似乎解決了
                     * 一對多的問題。但是也有問題,調用 getName(beanName) 時,到底返回哪個 bean 
                     * 實例好呢?
                     */
                    if (!beanName.equals(parentBeanName)) {
                        /*
                         * 這裏再次調用 getMergedBeanDefinition,只不過參數值變爲了 
                         * parentBeanName,用於合併父 BeanDefinition 和爺爺輩的 
                         * BeanDefinition。如果爺爺輩的 BeanDefinition 仍有父 
                         * BeanDefinition,則繼續合併
                         */
                        pbd = getMergedBeanDefinition(parentBeanName);
                    }
                    else {
                        // 獲取父容器,並判斷,父容器的類型,若不是 ConfigurableBeanFactory 則判拋出異常
                        BeanFactory parent = getParentBeanFactory();
                        if (parent instanceof ConfigurableBeanFactory) {
                            pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
                        }
                        else {
                            throw new NoSuchBeanDefinitionException(parentBeanName,
                                    "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
                                    "': cannot be resolved without an AbstractBeanFactory parent");
                        }
                    }
                }
                catch (NoSuchBeanDefinitionException ex) {
                    throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
                            "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
                }
                // 以父 BeanDefinition 的配置信息爲藍本創建 RootBeanDefinition,也就是“已合併的 BeanDefinition”
                mbd = new RootBeanDefinition(pbd);
                // 用子 BeanDefinition 中的屬性覆蓋父 BeanDefinition 中的屬性
                mbd.overrideFrom(bd);
            }

            // 如果用戶未配置 scope 屬性,則默認將該屬性配置爲 singleton
            if (!StringUtils.hasLength(mbd.getScope())) {
                mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
            }

            if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
                mbd.setScope(containingBd.getScope());
            }

            if (containingBd == null && isCacheBeanMetadata()) {
                // 緩存合併後的 BeanDefinition
                this.mergedBeanDefinitions.put(beanName, mbd);
            }
        }

        return mbd;
    }
}

2.5 獲取單例

上面的getSingleton方法是從緩存中獲取單例的過程,那麼如果緩存中不存在已經加載的單例bean,就需要從頭開始bean的加載過程了,而Spring中使用getSingleton的重載方法實現bean的加載過程。

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 + "'");
            }
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                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;
    }
}

上述的代碼其實使用了回調方法,使得程序可以在單例創建前後可以做一些準備以及處理操作,而真正的獲取單例bean的方法其實不是在此方法中實現的,其實現邏輯實在ObjectFactory類型的實例singletonFactory中實現的。而這些準備及處理操作包含以下內容:

1.檢查緩存是否已經加載過

2.若沒有加載,則記錄該beanName的正在加載狀態

3.加載單例前記錄加載狀態,也就是通過beforeSingletonCreation(beanName)方法記錄加載狀態

4.通過調用參數傳入的ObjectFactory的個體Object方法實例化bean

5.加載單例後的處理方法調用,通過afterSingletonCreation(beanName)方法移除緩存中對該bean的正在加載狀態的記錄

6.通過 addSingleton(beanName, singletonObject)將結果記錄至緩存並刪除加載bean過程中所記錄的各種輔助狀態。

7.返回處理結果

2.6 從FactoryBean中獲取bean實例

在getBean方法中,getObjectForBeanInstance是一個高頻率使用的方法,無論是從緩存中獲得bean還是根據不同的scope策略加載bean。總之,我們得到的bean實例後要做的第一步就是調用這個方法檢測一下正確性,其實就是檢測當前的bean是否是FactoryBean類型的bean,如果是,那麼需要調用該bean對應的FactoryBean實例中getObject()作爲返回值。

無論從緩存中獲得到的bean還是通過不同scope策略加載的bean都只是最原始的bean狀態,並不是我們最終想要的那個bean。舉個例子,假設我們需要對工廠bean進行處理,那麼這裏得到的其實是bean的初始狀態,但是我們真正需要的是工廠bean中定義的factory-method方法中返回的bean,而getObjectForBeanInstance方法就是完成這個工作的。

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

    // 如果 name 以 & 開頭,但 beanInstance 卻不是 FactoryBean,則認爲有問題。
    if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
        throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
    }

    /* 
     * 如果上面的判斷通過了,表明 beanInstance 可能是一個普通的 bean,也可能是一個 
     * FactoryBean。如果是一個普通的 bean,這裏直接返回 beanInstance 即可。如果是 
     * FactoryBean,則要調用工廠方法生成一個 bean 實例。
     */
    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
        return beanInstance;
    }

    Object object = null;
    if (mbd == null) {
        /*
         * 如果 mbd 爲空,則從緩存中加載 bean。FactoryBean 生成的單例 bean 會被緩存
         * 在 factoryBeanObjectCache 集合中,不用每次都創建
         */
        object = getCachedObjectForFactoryBean(beanName);
    }
    if (object == null) {
        // 經過前面的判斷,到這裏可以保證 beanInstance 是 FactoryBean 類型的,所以可以進行類型轉換
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        // 如果 mbd 爲空,則判斷是否存在名字爲 beanName 的 BeanDefinition
        if (mbd == null && containsBeanDefinition(beanName)) {
            // 合併 BeanDefinition
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        boolean synthetic = (mbd != null && mbd.isSynthetic());

        // 調用 getObjectFromFactoryBean 方法繼續獲取實例
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}

getObjectForBeanInstance中要做的工作:

1.對FactoryBean正確性的驗證。

2.對非FactoryBean不做任何處理。

3.對bean進行轉換。

4.將從Factory中解析bean的工作委託給getObjectFormFactoryBean。

getObjectFormFactoryBean源碼如下:

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    /*
     * FactoryBean 也有單例和非單例之分,針對不同類型的 FactoryBean,這裏有兩種處理方式:
     *   1. 單例 FactoryBean 生成的 bean 實例也認爲是單例類型。需放入緩存中,供後續重複使用
     *   2. 非單例 FactoryBean 生成的 bean 實例則不會被放入緩存中,每次都會創建新的實例
     */
    if (factory.isSingleton() && containsSingleton(beanName)) {
        synchronized (getSingletonMutex()) {
            // 從緩存中取 bean 實例,避免多次創建 bean 實例
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                // 使用工廠對象中創建實例
                object = doGetObjectFromFactoryBean(factory, beanName);
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if (alreadyThere != null) {
                    object = alreadyThere;
                }
                else {
                    // shouldPostProcess 等價於上一個方法中的 !synthetic,用於表示是否應用後置處理
                    if (object != null && shouldPostProcess) {
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            return object;
                        }
                        beforeSingletonCreation(beanName);
                        try {
                            // 應用後置處理
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw new BeanCreationException(beanName,
                                    "Post-processing of FactoryBean's singleton object failed", ex);
                        }
                        finally {
                            afterSingletonCreation(beanName);
                        }
                    }
                    // 這裏的 beanName 對應於 FactoryBean 的實現類, FactoryBean 的實現類也會被實例化,並被緩存在 singletonObjects 中
                    if (containsSingleton(beanName)) {
                        // FactoryBean 所創建的實例會被緩存在 factoryBeanObjectCache 中,供後續調用使用
                        this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
                    }
                }
            }
            return (object != NULL_OBJECT ? object : null);
        }
    }
    // 獲取非單例實例
    else {
        // 從工廠類中獲取實例
        Object object = doGetObjectFromFactoryBean(factory, beanName);
        if (object != null && shouldPostProcess) {
            try {
                // 應用後置處理
                object = postProcessObjectFromFactoryBean(object, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
            }
        }
        return object;
    }
}

在這個方法裏只做了一件事情,就是返回的bean如果是單例的,那就必須保證全局唯一,同時,也因爲是單例的,所以不必重複創建,可以使用緩存來提高性能,也就是說已經加載過就要就要記錄下來以便下次複用,否則的話,直接獲取了。

doGetObjectFromFactoryBean方法解析:

private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
        throws BeanCreationException {

    Object object;
    try {
        // if 分支的邏輯是 Java 安全方面的代碼,可以忽略,直接看 else 分支的代碼
        if (System.getSecurityManager() != null) {
            AccessControlContext acc = getAccessControlContext();
            try {
                object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                    @Override
                    public Object run() throws Exception {
                            return factory.getObject();
                        }
                    }, acc);
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            // 調用工廠方法生成 bean 實例
            object = factory.getObject();
        }
    }
    catch (FactoryBeanNotInitializedException ex) {
        throw new BeanCurrentlyInCreationException(beanName, ex.toString());
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
    }

    if (object == null && isSingletonCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(
                beanName, "FactoryBean which is currently in creation returned null from getObject");
    }
    return object;
}

如果bean聲明爲FactoryBean類型,則當提取bean時提取的不是FactoryBean,而是FactoryBean對應的getObject方法返回的bean,而doGetObjectFromFactoryBean正是實現這個功能的。

得到了返回結果並沒有直接返回,又做了後處理的操作,這個在後面會解析到。

getObjectForBeanInstance及它所調用的方法主要做了如下幾件事情:

1.檢測參數beanInstance的類型,如果是非FactoryBean類型的bean,直接返回

2.檢測FactoryBean實現類是否單例類型,針對單例和非單例進行不同處理

3.對於單例FactoryBean,先從緩存中獲取FactoryBean生成的實例

4.若緩存未命中,則調用FactoryBean.getObject()方法生成實例,並放入緩存中

5.對於非單例的FactoryBean,每次直接創建新的實例,無需緩存

6.如果shouldPostProcess=true,不管是單例還是非單例FactoryBean生成的實例,都要進行後置處理。

 

本文參考:Spring IOC 容器源碼分析 - 獲取單例 bean

                  《Spring源碼深度解析》

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