成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Spring IOC 容器源碼分析 - 獲取單例 bean

lufficc / 2820人閱讀

摘要:簡(jiǎn)介為了寫容器源碼分析系列的文章,我特地寫了一篇容器的導(dǎo)讀文章。在做完必要的準(zhǔn)備工作后,從本文開始,正式開始進(jìn)入源碼分析的階段。從緩存中獲取單例。返回以上就是和兩個(gè)方法的分析。

1. 簡(jiǎn)介

為了寫 Spring IOC 容器源碼分析系列的文章,我特地寫了一篇 Spring IOC 容器的導(dǎo)讀文章。在導(dǎo)讀一文中,我介紹了 Spring 的一些特性以及閱讀 Spring 源碼的一些建議。在做完必要的準(zhǔn)備工作后,從本文開始,正式開始進(jìn)入源碼分析的階段。

在本篇文章中,我將會(huì)詳細(xì)分析BeanFactorygetBean(String)方法實(shí)現(xiàn)細(xì)節(jié),getBean(String) 及所調(diào)用的方法總體來說實(shí)現(xiàn)上較為復(fù)雜,代碼長(zhǎng)度比較長(zhǎng)。作為源碼分析文章,本文的文章長(zhǎng)度也會(huì)比較長(zhǎng),希望大家耐心讀下去。

好了,其他的不多說了,進(jìn)入主題環(huán)節(jié)吧。

2. 源碼分析

簡(jiǎn)單說一下本章的內(nèi)容安排吧,在本章的開始,也就是2.1節(jié),我將會(huì)分析getBean(String)方法整體的實(shí)現(xiàn)邏輯。但不會(huì)分析它所調(diào)用的方法,這些方法將會(huì)在后續(xù)幾節(jié)中依次進(jìn)行分析。那接下來,我們就先來看看 getBean(String) 方法是如何實(shí)現(xiàn)的吧。

2.1 俯瞰 getBean(String) 源碼

在本小節(jié),我們先從戰(zhàn)略上俯瞰 getBean(String) 方法的實(shí)現(xiàn)源碼。代碼如下:

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

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

    /*
     * 通過 name 獲取 beanName。這里不使用 name 直接作為 beanName 有兩點(diǎn)原因:
     * 1. name 可能會(huì)以 & 字符開頭,表明調(diào)用者想獲取 FactoryBean 本身,而非 FactoryBean 
     *    實(shí)現(xiàn)類所創(chuàng)建的 bean。在 BeanFactory 中,F(xiàn)actoryBean 的實(shí)現(xiàn)類和其他的 bean 存儲(chǔ)
     *    方式是一致的,即 ,beanName 中是沒有 & 這個(gè)字符的。所以我們需要
     *    將 name 的首字符 & 移除,這樣才能從緩存里取到 FactoryBean 實(shí)例。
     * 2. 若 name 是一個(gè)別名,則應(yīng)將別名轉(zhuǎn)換為具體的實(shí)例名,也就是 beanName。
     */
    final String beanName = transformedBeanName(name);
    Object bean;

    /*
     * 從緩存中獲取單例 bean。Spring 是使用 Map 作為 beanName 和 bean 實(shí)例的緩存的,所以這
     * 里暫時(shí)可以把 getSingleton(beanName) 等價(jià)于 beanMap.get(beanName)。當(dāng)然,實(shí)際的
     * 邏輯并非如此簡(jiǎn)單,后面再細(xì)說。
     */
    Object sharedInstance = getSingleton(beanName);

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

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

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

        try {
            // 合并父 BeanDefinition 與子 BeanDefinition,后面會(huì)多帶帶分析這個(gè)方法
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            checkMergedBeanDefinition(mbd, beanName, args);

            // 檢查是否有 dependsOn 依賴,如果有則先初始化所依賴的 bean
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                    /*
                     * 檢測(cè)是否存在 depends-on 循環(huán)依賴,若存在則拋異常。比如 A 依賴 B,
                     * B 又依賴 A,他們的配置如下:
                     *   
                     *   
                     *   
                     * beanA 要求 beanB 在其之前被創(chuàng)建,但 beanB 又要求 beanA 先于它
                     * 創(chuàng)建。這個(gè)時(shí)候形成了循環(huán),對(duì)于 depends-on 循環(huán),Spring 會(huì)直接
                     * 拋出異常
                     */
                    if (isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Circular depends-on relationship between "" + beanName + "" and "" + dep + """);
                    }
                    // 注冊(cè)依賴記錄
                    registerDependentBean(dep, beanName);
                    try {
                        // 加載 depends-on 依賴
                        getBean(dep);
                    } 
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                """ + beanName + "" depends on missing bean "" + dep + """, ex);
                    }
                }
            }

            // 創(chuàng)建 bean 實(shí)例
            if (mbd.isSingleton()) {
                /*
                 * 這里并沒有直接調(diào)用 createBean 方法創(chuàng)建 bean 實(shí)例,而是通過 
                 * getSingleton(String, ObjectFactory) 方法獲取 bean 實(shí)例。
                 * getSingleton(String, ObjectFactory) 方法會(huì)在內(nèi)部調(diào)用 
                 * ObjectFactory 的 getObject() 方法創(chuàng)建 bean,并會(huì)在創(chuàng)建完成后,
                 * 將 bean 放入緩存中。關(guān)于 getSingleton 方法的分析,本文先不展開,我會(huì)在
                 * 后面的文章中進(jìn)行分析
                 */
                sharedInstance = getSingleton(beanName, new ObjectFactory() {
                    @Override
                    public Object getObject() throws BeansException {
                        try {
                            // 創(chuàng)建 bean 實(shí)例
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            destroySingleton(beanName);
                            throw ex;
                        }
                    }
                });
                // 如果 bean 是 FactoryBean 類型,則調(diào)用工廠方法獲取真正的 bean 實(shí)例。否則直接返回 bean 實(shí)例
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }

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

            // 創(chuàng)建其他類型的 bean 實(shí)例
            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() {
                        @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;
        }
    }

    // 如果需要進(jìn)行類型轉(zhuǎn)換,則在此處進(jìn)行轉(zhuǎn)換。類型轉(zhuǎn)換這一塊我沒細(xì)看,就不多說了。
    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;
}

以上就是getBean(String)doGetBean(String, Class, Object[], boolean)兩個(gè)方法的分析。代碼很長(zhǎng),需要一點(diǎn)耐心閱讀。為了凸顯方法的主邏輯,大家可以對(duì)代碼進(jìn)行一定的刪減,刪除一些日志和異常代碼,也可以刪除一些不是很重要的邏輯。另外由于 doGetBean 方法調(diào)用了其他的很多方法,在看代碼時(shí),經(jīng)常會(huì)忘掉 doGetBean 所調(diào)用的方法是怎么實(shí)現(xiàn)的。比如 getSingleton 方法出現(xiàn)了兩次,但兩個(gè)方法并不同,在看第二個(gè)的 getSingleton 方法時(shí),可能會(huì)忘掉第一個(gè) getSingleton 是怎么實(shí)現(xiàn)的。另外,如果你想對(duì)比兩個(gè)重載方法的異同,在 IDEA 里跳來跳去也是很不方便。為此,我使用了 sublime 進(jìn)行分屏,左屏是刪減后的 doGetBean 方法,右屏是 doGetBean 調(diào)用的一些方法,這樣看起來會(huì)方便一點(diǎn)。忘了某個(gè)方法的實(shí)現(xiàn)邏輯后,可以到右屏查看,也可進(jìn)行對(duì)比。分屏效果如下:

這里我為了演示,刪除了不少東西。大家可以按需進(jìn)行刪減,并配上注釋,輔助理解。

看完了源碼,下面我來簡(jiǎn)單總結(jié)一下 doGetBean 的執(zhí)行流程。如下:

轉(zhuǎn)換 beanName

從緩存中獲取實(shí)例

如果實(shí)例不為空,且 args = null。調(diào)用 getObjectForBeanInstance 方法,并按 name 規(guī)則返回相應(yīng)的 bean 實(shí)例

若上面的條件不成立,則到父容器中查找 beanName 對(duì)有的 bean 實(shí)例,存在則直接返回

若父容器中不存在,則進(jìn)行下一步操作 -- 合并 BeanDefinition

處理 depends-on 依賴

創(chuàng)建并緩存 bean

調(diào)用 getObjectForBeanInstance 方法,并按 name 規(guī)則返回相應(yīng)的 bean 實(shí)例

按需轉(zhuǎn)換 bean 類型,并返回轉(zhuǎn)換后的 bean 實(shí)例。

以上步驟對(duì)應(yīng)的流程圖如下:

2.2 beanName 轉(zhuǎn)換

在獲取 bean 實(shí)例之前,Spring 第一件要做的事情是對(duì)參數(shù) name 進(jìn)行轉(zhuǎn)換。轉(zhuǎn)換的目的主要是為了解決兩個(gè)問題,第一個(gè)是處理以字符 & 開頭的 name,防止 BeanFactory 無法找到與 name 對(duì)應(yīng)的 bean 實(shí)例。第二個(gè)是處理別名問題,Spring 不會(huì)存儲(chǔ) <別名, bean 實(shí)例> 這種映射,僅會(huì)存儲(chǔ) 。所以,同樣是為了避免 BeanFactory 找不到 name 對(duì)應(yīng)的 bean 的實(shí)例,對(duì)于別名也要進(jìn)行轉(zhuǎn)換。接下來,我們來簡(jiǎn)單分析一下轉(zhuǎn)換的過程,如下:

protected String transformedBeanName(String name) {
    // 這里調(diào)用了兩個(gè)方法: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;
    // 循環(huán)處理 & 字符。比如 name = "&&&&&helloService",最終會(huì)被轉(zhuǎn)成 helloService
    while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
        beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
    }
    return beanName;
}

/** 該方法用于轉(zhuǎn)換別名 */
public String canonicalName(String name) {
    String canonicalName = name;
    String resolvedName;
    /*
     * 這里使用 while 循環(huán)進(jìn)行處理,原因是:可能會(huì)存在多重別名的問題,即別名指向別名。比如下面
     * 的配置:
     *   
     *   
     *   
     *
     * 上面的別名指向關(guān)系為 aliasB -> aliasA -> hello,對(duì)于上面的別名配置,aliasMap 中數(shù)據(jù)
     * 視圖為:aliasMap = [, ]。通過下面的循環(huán)解析別名
     * aliasB 最終指向的 beanName
     */
    do {
        resolvedName = this.aliasMap.get(canonicalName);
        if (resolvedName != null) {
                canonicalName = resolvedName;
        }
    }
    while (resolvedName != null);
    return canonicalName;
}
2.3 從緩存中獲取 bean 實(shí)例

對(duì)于單例 bean,Spring 容器只會(huì)實(shí)例化一次。后續(xù)再次獲取時(shí),只需直接從緩存里獲取即可,無需且不能再次實(shí)例化(否則單例就沒意義了)。從緩存中取 bean 實(shí)例的方法是getSingleton(String),下面我們就來看看這個(gè)方法實(shí)現(xiàn)方式吧。如下:

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

/**
 * 這里解釋一下 allowEarlyReference 參數(shù),allowEarlyReference 表示是否允許其他 bean 引用
 * 正在創(chuàng)建中的 bean,用于處理循環(huán)引用的問題。關(guān)于循環(huán)引用,這里先簡(jiǎn)單介紹一下。先看下面的配置:
 *
 *   
 *       
 *   
 *   
 *       
 *   
 * 
 * 如上所示,hello 依賴 world,world 又依賴于 hello,他們之間形成了循環(huán)依賴。Spring 在構(gòu)建 
 * hello 這個(gè) bean 時(shí),會(huì)檢測(cè)到它依賴于 world,于是先去實(shí)例化 world。實(shí)例化 world 時(shí),發(fā)現(xiàn) 
 * world 依賴 hello。這個(gè)時(shí)候容器又要去初始化 hello。由于 hello 已經(jīng)在初始化進(jìn)程中了,為了讓 
 * world 能完成初始化,這里先讓 world 引用正在初始化中的 hello。world 初始化完成后,hello 
 * 就可引用到 world 實(shí)例,這樣 hello 也就能完成初始了。關(guān)于循環(huán)依賴,我后面會(huì)專門寫一篇文章講
 * 解,這里先說這么多。
 */
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 從 singletonObjects 獲取實(shí)例,singletonObjects 中緩存的實(shí)例都是完全實(shí)例化好的 bean,可以直接使用
    Object singletonObject = this.singletonObjects.get(beanName);
    /*
     * 如果 singletonObject = null,表明還沒創(chuàng)建,或者還沒完全創(chuàng)建好。
     * 這里判斷 beanName 對(duì)應(yīng)的 bean 是否正在創(chuàng)建中
     */
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 從 earlySingletonObjects 中獲取提前曝光的 bean,用于處理循環(huán)引用
            singletonObject = this.earlySingletonObjects.get(beanName);
            // 如果如果 singletonObject = null,且允許提前曝光 bean 實(shí)例,則從相應(yīng)的 ObjectFactory 獲取一個(gè)原始的(raw)bean(尚未填充屬性)
            if (singletonObject == null && allowEarlyReference) {
                // 獲取相應(yīng)的工廠類
                ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 提前曝光 bean 實(shí)例,用于解決循環(huán)依賴
                    singletonObject = singletonFactory.getObject();
                    // 放入緩存中,如果還有其他 bean 依賴當(dāng)前 bean,其他 bean 可以直接從 earlySingletonObjects 取結(jié)果
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

上面的代碼雖然不長(zhǎng),但是涉及到了好幾個(gè)緩存集合。如果不知道這些緩存的用途是什么,上面源碼可能就很難弄懂了。這幾個(gè)緩存集合用的很頻繁,在后面的代碼中還會(huì)出現(xiàn),所以這里介紹一下。如下:

緩存 用途
singletonObjects 用于存放完全初始化好的 bean,從該緩存中取出的 bean 可以直接使用
earlySingletonObjects 用于存放還在初始化中的 bean,用于解決循環(huán)依賴
singletonFactories 用于存放 bean 工廠。bean 工廠所產(chǎn)生的 bean 是還未完成初始化的 bean。如代碼所示,bean 工廠所生成的對(duì)象最終會(huì)被緩存到?earlySingletonObjects 中

關(guān)于 getSingleton 先說到這里,getSingleton 源碼并不多。但涉及到了循環(huán)依賴的相關(guān)邏輯,如果對(duì)這一塊不理解可能不知道代碼所云。等后面分析循環(huán)依賴的時(shí)候,我會(huì)再次分析這個(gè)方法,所以暫時(shí)不理解也沒關(guān)系。

2.4 合并父 BeanDefinition 與子 BeanDefinition

Spring 支持配置繼承,在 標(biāo)簽中可以使用parent屬性配置父類 bean。這樣子類 bean 可以繼承父類 bean 的配置信息,同時(shí)也可覆蓋父類中的配置。比如下面的配置:


    



    

如上所示,hello-child 配置繼承自 hello。hello-child 未配置 class 屬性,這里我們讓它繼承父配置中的 class 屬性。然后我們寫點(diǎn)代碼測(cè)試一下,如下:

String configLocation = "application-parent-bean.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);
System.out.println("hello -> " + applicationContext.getBean("hello"));
System.out.println("hello-child -> " + applicationContext.getBean("hello-child"));

測(cè)試結(jié)果如下:

由測(cè)試結(jié)果可以看出,hello-child 在未配置 class 的屬性下也實(shí)例化成功了,表明它成功繼承了父配置的 class 屬性。

看完代碼演示,接下來我們來看看源碼吧。如下:

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

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

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

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

        // 我暫時(shí)還沒去詳細(xì)了解 containingBd 的用途,盡管從方法的注釋上可以知道 containingBd 的大致用途,但沒經(jīng)過詳細(xì)分析,就不多說了。見諒
        if (containingBd == null) {
            mbd = this.mergedBeanDefinitions.get(beanName);
        }

        if (mbd == null) {
            // bd.getParentName() == null,表明無父配置,這時(shí)直接將當(dāng)前的 BeanDefinition 升級(jí)為 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 一定
                     * 在父容器中。原因也很簡(jiǎn)單,容器底層是用 Map 緩存  鍵值對(duì)
                     * 的。同一個(gè)容器下,使用同一個(gè) beanName 映射兩個(gè) bean 實(shí)例顯然是不合適的。
                     * 有的朋友可能會(huì)覺得可以這樣存儲(chǔ): ,似乎解決了
                     * 一對(duì)多的問題。但是也有問題,調(diào)用 getName(beanName) 時(shí),到底返回哪個(gè) bean 
                     * 實(shí)例好呢?
                     */
                    if (!beanName.equals(parentBeanName)) {
                        /*
                         * 這里再次調(diào)用 getMergedBeanDefinition,只不過參數(shù)值變?yōu)榱?
                         * parentBeanName,用于合并父 BeanDefinition 和爺爺輩的 
                         * BeanDefinition。如果爺爺輩的 BeanDefinition 仍有父 
                         * BeanDefinition,則繼續(xù)合并
                         */
                        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 的配置信息為藍(lán)本創(chuàng)建 RootBeanDefinition,也就是“已合并的 BeanDefinition”
                mbd = new RootBeanDefinition(pbd);
                // 用子 BeanDefinition 中的屬性覆蓋父 BeanDefinition 中的屬性
                mbd.overrideFrom(bd);
            }

            // 如果用戶未配置 scope 屬性,則默認(rèn)將該屬性配置為 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;
    }
}

上面的源碼雖然有點(diǎn)長(zhǎng),但好在邏輯不是很復(fù)雜。加上我在源碼里進(jìn)行了比較詳細(xì)的注解,我想耐心看一下還是可以看懂的,這里就不多說了。

2.5 從 FactoryBean 中獲取 bean 實(shí)例

在經(jīng)過前面這么多的步驟處理后,到這里差不多就接近 doGetBean 方法的尾聲了。在本節(jié)中,我們來看看從 FactoryBean 實(shí)現(xiàn)類中獲取 bean 實(shí)例的過程。關(guān)于 FactoryBean 的用法,我在導(dǎo)讀那篇文章中已經(jīng)演示過,這里就不再次說明了。那接下來,我們直入主題吧,相關(guān)的源碼如下:

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

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

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

    Object object = null;
    if (mbd == null) {
        /*
         * 如果 mbd 為空,則從緩存中加載 bean。FactoryBean 生成的單例 bean 會(huì)被緩存
         * 在 factoryBeanObjectCache 集合中,不用每次都創(chuàng)建
         */
        object = getCachedObjectForFactoryBean(beanName);
    }
    if (object == null) {
        // 經(jīng)過前面的判斷,到這里可以保證 beanInstance 是 FactoryBean 類型的,所以可以進(jìn)行類型轉(zhuǎn)換
        FactoryBean factory = (FactoryBean) beanInstance;
        // 如果 mbd 為空,則判斷是否存在名字為 beanName 的 BeanDefinition
        if (mbd == null && containsBeanDefinition(beanName)) {
            // 合并 BeanDefinition
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        // synthetic 字面意思是"合成的"。通過全局查找,我發(fā)現(xiàn)在 AOP 相關(guān)的類中會(huì)將該屬性設(shè)為 true。
        // 所以我覺得該字段可能表示某個(gè) bean 是不是被 AOP 增強(qiáng)過,也就是 AOP 基于原始類合成了一個(gè)新的代理類。
        // 不過目前只是猜測(cè),沒有深究。如果有朋友知道這個(gè)字段的具體意義,還望不吝賜教
        boolean synthetic = (mbd != null && mbd.isSynthetic());

        // 調(diào)用 getObjectFromFactoryBean 方法繼續(xù)獲取實(shí)例
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}

protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName, boolean shouldPostProcess) {
    /*
     * FactoryBean 也有單例和非單例之分,針對(duì)不同類型的 FactoryBean,這里有兩種處理方式:
     *   1. 單例 FactoryBean 生成的 bean 實(shí)例也認(rèn)為是單例類型。需放入緩存中,供后續(xù)重復(fù)使用
     *   2. 非單例 FactoryBean 生成的 bean 實(shí)例則不會(huì)被放入緩存中,每次都會(huì)創(chuàng)建新的實(shí)例
     */
    if (factory.isSingleton() && containsSingleton(beanName)) {
        synchronized (getSingletonMutex()) {
            // 從緩存中取 bean 實(shí)例,避免多次創(chuàng)建 bean 實(shí)例
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                // 使用工廠對(duì)象中創(chuàng)建實(shí)例
                object = doGetObjectFromFactoryBean(factory, beanName);
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if (alreadyThere != null) {
                    object = alreadyThere;
                }
                else {
                    // shouldPostProcess 等價(jià)于上一個(gè)方法中的 !synthetic,用于表示是否應(yīng)用后置處理
                    if (object != null && shouldPostProcess) {
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            return object;
                        }
                        beforeSingletonCreation(beanName);
                        try {
                            // 應(yīng)用后置處理
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw new BeanCreationException(beanName,
                                    "Post-processing of FactoryBean"s singleton object failed", ex);
                        }
                        finally {
                            afterSingletonCreation(beanName);
                        }
                    }
                    // 這里的 beanName 對(duì)應(yīng)于 FactoryBean 的實(shí)現(xiàn)類, FactoryBean 的實(shí)現(xiàn)類也會(huì)被實(shí)例化,并被緩存在 singletonObjects 中
                    if (containsSingleton(beanName)) {
                        // FactoryBean 所創(chuàng)建的實(shí)例會(huì)被緩存在 factoryBeanObjectCache 中,供后續(xù)調(diào)用使用
                        this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
                    }
                }
            }
            return (object != NULL_OBJECT ? object : null);
        }
    }
    // 獲取非單例實(shí)例
    else {
        // 從工廠類中獲取實(shí)例
        Object object = doGetObjectFromFactoryBean(factory, beanName);
        if (object != null && shouldPostProcess) {
            try {
                // 應(yīng)用后置處理
                object = postProcessObjectFromFactoryBean(object, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(beanName, "Post-processing of FactoryBean"s object failed", ex);
            }
        }
        return object;
    }
}

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() {
                    @Override
                    public Object run() throws Exception {
                            return factory.getObject();
                        }
                    }, acc);
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            // 調(diào)用工廠方法生成 bean 實(shí)例
            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;
}

上面的源碼分析完了,代碼雖長(zhǎng),但整體邏輯不是很復(fù)雜,這里簡(jiǎn)單總結(jié)一下。getObjectForBeanInstance 及它所調(diào)用的方法主要做了如下幾件事情:

檢測(cè)參數(shù) beanInstance 的類型,如果是非 FactoryBean 類型的 bean,直接返回

檢測(cè) FactoryBean 實(shí)現(xiàn)類是否單例類型,針對(duì)單例和非單例類型進(jìn)行不同處理

對(duì)于單例 FactoryBean,先從緩存里獲取 FactoryBean 生成的實(shí)例

若緩存未命中,則調(diào)用 FactoryBean.getObject() 方法生成實(shí)例,并放入緩存中

對(duì)于非單例的 FactoryBean,每次直接創(chuàng)建新的實(shí)例即可,無需緩存

如果 shouldPostProcess = true,不管是單例還是非單例 FactoryBean 生成的實(shí)例,都要進(jìn)行后置處理

本節(jié)涉及到了 FactoryBean 和后置處理兩個(gè)特性,關(guān)于這兩個(gè)特性,不熟悉的同學(xué)可以參考我在導(dǎo)讀一文中的說明,這里就不過多解釋了。

3. 總結(jié)

到這里,Spring IOC 容器獲取 bean 實(shí)例這一塊的內(nèi)容就分析完了。如果大家是初次閱讀 Spring 的源碼,看不懂也沒關(guān)系。多看幾遍,認(rèn)證思考一下,相信是能看得懂的。另外由于本人水平有限,以上的源碼分析有誤的地方,還望多指教,謝了。

好了,本文先到這里。又到周五了,祝大家在即將到來的周末玩的開心。over.

參考

《Spring 源碼深度解析》- 郝佳著

附錄:Spring 源碼分析文章列表 Ⅰ. IOC
更新時(shí)間 標(biāo)題
2018-05-30 Spring IOC 容器源碼分析系列文章導(dǎo)讀
2018-06-01 Spring IOC 容器源碼分析 - 獲取單例 bean
2018-06-04 Spring IOC 容器源碼分析 - 創(chuàng)建單例 bean 的過程
2018-06-06 Spring IOC 容器源碼分析 - 創(chuàng)建原始 bean 對(duì)象
2018-06-08 Spring IOC 容器源碼分析 - 循環(huán)依賴的解決辦法
2018-06-11 Spring IOC 容器源碼分析 - 填充屬性到 bean 原始對(duì)象
2018-06-11 Spring IOC 容器源碼分析 - 余下的初始化工作
Ⅱ. AOP
更新時(shí)間 標(biāo)題
2018-06-17 Spring AOP 源碼分析系列文章導(dǎo)讀
2018-06-20 Spring AOP 源碼分析 - 篩選合適的通知器
2018-06-20 Spring AOP 源碼分析 - 創(chuàng)建代理對(duì)象
2018-06-22 Spring AOP 源碼分析 - 攔截器鏈的執(zhí)行過程
Ⅲ. MVC
更新時(shí)間 標(biāo)題
2018-06-29 Spring MVC 原理探秘 - 一個(gè)請(qǐng)求的旅行過程
2018-06-30 Spring MVC 原理探秘 - 容器的創(chuàng)建過程
本文在知識(shí)共享許可協(xié)議 4.0 下發(fā)布,轉(zhuǎn)載需在明顯位置處注明出處
作者:coolblog.xyz
本文同步發(fā)布在我的個(gè)人博客:http://www.coolblog.xyz


本作品采用知識(shí)共享署名-非商業(yè)性使用-禁止演繹 4.0 國(guó)際許可協(xié)議進(jìn)行許可。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69604.html

相關(guān)文章

  • Spring專題之Bean初始化源碼分析(1)

    摘要:初始化我們知道容器初始化后會(huì)對(duì)容器中非懶加載的,單例的以及非抽象的定義進(jìn)行的初始化操作,所以我們分析源碼的入口也就是在容器初始化的入口,分析容器初始化后在什么地方開始第一次的初始化。 前言 Spring IOC容器在初始化之后會(huì)對(duì)容器中非懶加載的,單例的以及非抽象的bean定義進(jìn)行bean的初始化操作,同時(shí)會(huì)也涉及到Bean的后置處理器以及DI(依賴注入)等行為。對(duì)于Bean的初始化,...

    harryhappy 評(píng)論0 收藏0
  • Spring IOC 容器源碼分析 - 創(chuàng)建單例 bean 的過程

    摘要:關(guān)于創(chuàng)建實(shí)例的過程,我將會(huì)分幾篇文章進(jìn)行分析。源碼分析創(chuàng)建實(shí)例的入口在正式分析方法前,我們先來看看方法是在哪里被調(diào)用的。時(shí),表明方法不存在,此時(shí)拋出異常。該變量用于表示是否提前暴露單例,用于解決循環(huán)依賴。 1. 簡(jiǎn)介 在上一篇文章中,我比較詳細(xì)的分析了獲取 bean 的方法,也就是getBean(String)的實(shí)現(xiàn)邏輯。對(duì)于已實(shí)例化好的單例 bean,getBean(String) ...

    mochixuan 評(píng)論0 收藏0
  • Spring IOC 容器源碼分析系列文章導(dǎo)讀

    摘要:本文是容器源碼分析系列文章的第一篇文章,將會(huì)著重介紹的一些使用方法和特性,為后續(xù)的源碼分析文章做鋪墊。我們可以通過這兩個(gè)別名獲取到這個(gè)實(shí)例,比如下面的測(cè)試代碼測(cè)試結(jié)果如下本小節(jié),我們來了解一下這個(gè)特性。 1. 簡(jiǎn)介 Spring 是一個(gè)輕量級(jí)的企業(yè)級(jí)應(yīng)用開發(fā)框架,于 2004 年由 Rod Johnson 發(fā)布了 1.0 版本。經(jīng)過十幾年的迭代,現(xiàn)在的 Spring 框架已經(jīng)非常成熟了...

    NSFish 評(píng)論0 收藏0
  • Spring IOC 容器源碼分析 - 循環(huán)依賴的解決辦法

    摘要:實(shí)例化時(shí),發(fā)現(xiàn)又依賴于。一些緩存的介紹在進(jìn)行源碼分析前,我們先來看一組緩存的定義??墒强赐暝创a后,我們似乎仍然不知道這些源碼是如何解決循環(huán)依賴問題的。 1. 簡(jiǎn)介 本文,我們來看一下 Spring 是如何解決循環(huán)依賴問題的。在本篇文章中,我會(huì)首先向大家介紹一下什么是循環(huán)依賴。然后,進(jìn)入源碼分析階段。為了更好的說明 Spring 解決循環(huán)依賴的辦法,我將會(huì)從獲取 bean 的方法getB...

    aikin 評(píng)論0 收藏0
  • Spring IOC 容器源碼分析 - 余下的初始化工作

    摘要:簡(jiǎn)介本篇文章是容器源碼分析系列文章的最后一篇文章,本篇文章所分析的對(duì)象是方法,該方法用于對(duì)已完成屬性填充的做最后的初始化工作。后置處理器是拓展點(diǎn)之一,通過實(shí)現(xiàn)后置處理器接口,我們就可以插手的初始化過程。 1. 簡(jiǎn)介 本篇文章是Spring IOC 容器源碼分析系列文章的最后一篇文章,本篇文章所分析的對(duì)象是 initializeBean 方法,該方法用于對(duì)已完成屬性填充的 bean 做最...

    Alfred 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<