[Spring]Spring的getBean路線-createBean

前言

承上啟下:

  1. Spring的refresh->finishBeanFactoryInitialization(容器初始化單例非延遲加載Bean的入口).
  2. beanFactory.preInstantiateSingletons();代态,Bean工廠開始加載Bean實例.
  3. 獲取beanName列表吐限,循環(huán)對每個beanName進行getBean操作.
  4. getBean中調(diào)用了doGetBean進行了一些創(chuàng)建Bean的前置處理.最終在getSingleton中調(diào)用了createBean進行Bean的創(chuàng)建.

OK,下面進入今天的主題-createBean.

createBean的總體流程

  • 解析Bean類型,獲取Class: resolveBeanClass.
  • 檢查和準備Bean中的方法覆蓋: prepareMethodOverrides.
  • 應(yīng)用實例化之前的后處理器: resolveBeforeInstantiation.
  • 開始創(chuàng)建Bean: doCreateBean

1. resolveBeanClass-解析Bean類型,獲取Bean的Class.

  • AbstractAutowireCapableBeanFactory#createBean
    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {

        if (logger.isTraceEnabled()) {
            logger.trace("Creating instance of bean '" + beanName + "'");
        }
        RootBeanDefinition mbdToUse = mbd;

        // 確認當(dāng)前的Bean類是可以被解析的输瓜,并且支持克隆BeanDefinition,merged的BeanDefinition不在此方法的考慮范圍
        // 進入到里面敬察,會發(fā)現(xiàn)器通過了類加載器進行了Class的獲取
        Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
        if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
            // 克隆一份BeanDefinition,用來設(shè)置上加載出來的對象
            // 采用副本的原因是某些類可能需要動態(tài)加載Class
            mbdToUse = new RootBeanDefinition(mbd);
            mbdToUse.setBeanClass(resolvedClass);
        }
        // 省略若干代碼
}

容器在前面已經(jīng)加載并注冊好了BeanDefinition,它描述了關(guān)于Bean的信息回俐,在此處Spring要開始將自己抽象出來的BeanDefinition進行解析.這里思考一個問題,為什么Spring在加載Bean的時候選擇了RootBeanDefinition進行解析,而不是之前在加載BeanDefinition的時候反復(fù)說到的GenericBeanDefinition?

RootBeanDefinition對應(yīng)的是Java中的繼承體系而制造的復(fù)合BeanDefinition(Spring在定義Bean時域慷,也支持定義繼承關(guān)系).從各種容器加載出來的BeanDefinition环形,最終都會通過getMergedLocalBeanDefinition轉(zhuǎn)化成RootBeanDefinition.

RootBeanDefinition可以合成有繼承關(guān)系的BeanDefinition(如果沒有繼承關(guān)系,那么自己便是代表parent).

這里推薦一篇文章,希望可以帶來更多的思考: 【小家Spring】Spring IoC容器中核心定義之------BeanDefinition深入分析(RootBeanDefinition谤草、ChildBeanDefinition...)

OK,回到主題,拿到傳進來的RootBeanDefinition后跟束,Spring需要確認當(dāng)前的Bean類是可以被解析的,即是否能通過類加載器進行載入.對于注解容器來說,在組件掃描的時候就通過ResourceLoader進行了資源的定位丑孩,所以在這里是可以確認可以被解析的.
考慮到健全性冀宴,Spring還是謹慎地進行了校驗,畢竟不是所有的BeanDefinition都是通過注解容器加載的.

  • AbstractBeanFactory#resolveBeanClass
    protected Class<?> resolveBeanClass(RootBeanDefinition mbd, String beanName, Class<?>... typesToMatch)
            throws CannotLoadBeanClassException {

        try {
            // 如果當(dāng)前BeanDefinition存儲beanClass屬于Class實例,直接返回
            if (mbd.hasBeanClass()) {
                return mbd.getBeanClass();
            }
            if (System.getSecurityManager() != null) {
                return AccessController.doPrivileged((PrivilegedExceptionAction<Class<?>>)
                        () -> doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());
            }
            else {
                // 如果當(dāng)前BeanDefinition沒有存儲Class實例,那么嘗試解析
                return doResolveBeanClass(mbd, typesToMatch);
            }
        }
        catch (PrivilegedActionException pae) {
            ClassNotFoundException ex = (ClassNotFoundException) pae.getException();
            throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex);
        }
        catch (ClassNotFoundException ex) {
            throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex);
        }
        catch (LinkageError err) {
            throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), err);
        }
    }
  • AbstractBeanFactory#doResolveBeanClass
    private Class<?> doResolveBeanClass(RootBeanDefinition mbd, Class<?>... typesToMatch)
            throws ClassNotFoundException {

        ClassLoader beanClassLoader = getBeanClassLoader();
        ClassLoader dynamicLoader = beanClassLoader;
        boolean freshResolve = false;

        if (!ObjectUtils.isEmpty(typesToMatch)) {
            // When just doing type checks (i.e. not creating an actual instance yet),
            // use the specified temporary class loader (e.g. in a weaving scenario).
            ClassLoader tempClassLoader = getTempClassLoader();
            if (tempClassLoader != null) {
                dynamicLoader = tempClassLoader;
                freshResolve = true;
                if (tempClassLoader instanceof DecoratingClassLoader) {
                    DecoratingClassLoader dcl = (DecoratingClassLoader) tempClassLoader;
                    for (Class<?> typeToMatch : typesToMatch) {
                        dcl.excludeClass(typeToMatch.getName());
                    }
                }
            }
        }
        // 獲取BeanDefinition的className.
        String className = mbd.getBeanClassName();
        if (className != null) {
            // 根據(jù)className解析成表達式
            Object evaluated = evaluateBeanDefinitionString(className, mbd);
            if (!className.equals(evaluated)) {
                // A dynamically resolved expression, supported as of 4.2...
                if (evaluated instanceof Class) {
                    return (Class<?>) evaluated;
                }
                else if (evaluated instanceof String) {
                    className = (String) evaluated;
                    freshResolve = true;
                }
                else {
                    throw new IllegalStateException("Invalid class name expression result: " + evaluated);
                }
            }
            if (freshResolve) {
                // When resolving against a temporary class loader, exit early in order
                // to avoid storing the resolved Class in the bean definition.
                if (dynamicLoader != null) {
                    try {
                        // 如果解析后的表達式為Class則直接返回
                        return dynamicLoader.loadClass(className);
                    }
                    catch (ClassNotFoundException ex) {
                        if (logger.isTraceEnabled()) {
                            logger.trace("Could not load class [" + className + "] from " + dynamicLoader + ": " + ex);
                        }
                    }
                }
                // 反射工具類加載Class
                return ClassUtils.forName(className, dynamicLoader);
            }
        }

        // Resolve regularly, caching the result in the BeanDefinition...
        return mbd.resolveBeanClass(beanClassLoader);
    }

這里Spring會獲取到當(dāng)前工廠的類加載器温学,然后從BeanDefinition中獲取className.Spring會先對className做表達式解析略贮,如果不是通過表達式進行解析,那么調(diào)用ClassUtils.forName(className, dynamicLoader);,如果className不存在仗岖,再使用類加載器進行加載.類于類加載器是一一對應(yīng)的關(guān)系逃延,Spring需要找到可以加載該類的類加載器,然后加載出Class對象.

2. 檢查和準備Bean中的方法覆蓋-prepareMethodOverrides

  • AbstractAutowireCapableBeanFactory#createBean
        // Prepare method overrides.
        // 檢查和準備Bean中的方法覆蓋
        try {
            mbdToUse.prepareMethodOverrides();
        }
  • AbstractBeanDefinition#prepareMethodOverrides

主要針對lookup methods做校驗和方法覆蓋。現(xiàn)在用得比較少了.
如果對Lookup方法注入有興趣轧拄,可以在Spring的官網(wǎng)查看: 點我前往

  • AbstractBeanDefinition#prepareMethodOverrides
    public void prepareMethodOverrides() throws BeanDefinitionValidationException {
        // Check that lookup methods exist and determine their overloaded status.
        // 檢查查找方法是否存在揽祥,并確定其重載狀態(tài)。
        if (hasMethodOverrides()) {
            getMethodOverrides().getOverrides().forEach(this::prepareMethodOverride);
        }
    }
  • AbstractBeanDefinition#prepareMethodOverride

驗證并準備給定的方法是否被重寫紧帕。檢查是否存在具有指定名稱的方法盔然,如果找不到該方法,則將其標記為未重載是嗜。

    protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
        int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
        if (count == 0) {
            throw new BeanDefinitionValidationException(
                    "Invalid method override: no method with name '" + mo.getMethodName() +
                    "' on class [" + getBeanClassName() + "]");
        }
        // 如果不存在重載,在使用CGLIB增強階段就不需要校驗了
        else if (count == 1) {
            // Mark override as not overloaded, to avoid the overhead of arg type checking.
            // 將替代標記為未過載愈案,以避免arg類型檢查的開銷。
            mo.setOverloaded(false);
        }
    }

3. 激活Bean實例化前的后置處理器-resolveBeforeInstantiation

        try {
            // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
            // 解析BeanPostProcessors的BeanPostProcessorsBeforeInstantiation,試圖返回一個需要創(chuàng)建Bean的代理對象
            // resolveBeforeInstantiation只針對有自定義的targetsource.
            // 因為自定義的targetsource不是Spring的bean,那么后續(xù)的初始化與該bean無關(guān)
            // 因此直接可以進行代理
            Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
            if (bean != null) {
                return bean;
            }
        }
        catch (Throwable ex) {
            throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                    "BeanPostProcessor before instantiation of bean failed", ex);
        }
  • AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation
    protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
        Object bean = null;
        // 如果beforeInstantiationResolved為false
        if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
            // Make sure bean class is actually resolved at this point.
            // 如果注冊了InstantiationAwareBeanPostProcessors的BeanPostProcessor.
            if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                Class<?> targetType = determineTargetType(beanName, mbd);
                if (targetType != null) {
                    // 執(zhí)行實現(xiàn)了該后置處理器接口的類中postProcessBeforeInitialization方法
                    // Spring在這里使用了責(zé)任鏈模式,如果有一個后置處理器可以返回Object,就會馬上跳出循環(huán)
                    // 同時鹅搪,如果第三方框架在此后置處理器對Bean執(zhí)行了操作站绪,那么將不會進入到doCreateBean中了.
                    // 注意傳參,這里傳入的是一個class和beanName.
                    bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                    if (bean != null) {
                        bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                    }
                }
            }
            mbd.beforeInstantiationResolved = (bean != null);
        }
        return bean;
    }

如果當(dāng)前的Bean實現(xiàn)了InstantiationAwareBeanPostProcessor接口丽柿,并且在postProcessBeforeInstantiation中返回了Object恢准,那么Spring將不會執(zhí)行doCreateBean進行實例化,而是將當(dāng)前用戶返回的bean實例直接返回甫题。
AbstractAutoProxyCreator這個AOP代理的BeanPostProcessor便是這樣做的.
執(zhí)行完before的操作后馁筐,如果返回了實例,那么繼續(xù)執(zhí)行after的操作.
其中,before和after的執(zhí)行邏輯有點區(qū)別:
before方法執(zhí)行的中斷條件:

                Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
                if (result != null) {
                    return result;
                }

after方法執(zhí)行的中斷條件:

            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }

關(guān)注這個BeanPostPorcessor坠非,在后續(xù)的學(xué)習(xí)中敏沉,我們?nèi)阅茉谶^濾器中學(xué)習(xí)到這種職責(zé)鏈的設(shè)計原則.
我們來看看InstantiationAwareBeanPostProcessor的接口清單.默認返回null.

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

    @Nullable
    default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        return null;
    }

    default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }
}

4. 開始創(chuàng)建Bean-doCreateBean

OK,再梳理一下思路,從createBean中,Spring分析了當(dāng)前的Class是否能被類加載器加載,如果可以加載盟迟,返回當(dāng)前BeanDefinitino的Class對象,接著進行了Lookup method的檢查秋泳,最后激活了InstantiationAwareBeanPostProcessors的方法,如果以上方法都沒中斷,那么Spring將進行Bean的實例化-doCreateBean.

        try {
            // 創(chuàng)建bean的入口
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            if (logger.isTraceEnabled()) {
                logger.trace("Finished creating instance of bean '" + beanName + "'");
            }
            return beanInstance;
        }
        catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
            // A previously detected exception with proper bean creation context already,
            // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
        }

總結(jié)

  • 在createBean階段攒菠,Spring會分析當(dāng)前BeanDefinition的Class是否是可解析的.具體表現(xiàn)為用類加載器進行l(wèi)oadClass或者Class.forName
  • 檢查當(dāng)前BeanDefinition的Lookup method是否存在迫皱,并且確認重載狀態(tài).
  • 如果實現(xiàn)了InstantiationAwareBeanPostProcessors,激活此后置處理器,如果可以從此后置處理器中獲取Bean實例辖众,那么直接返回該Bean實例卓起,不會進行doCreateBean操作.
  • 開始Bean的實例化-doCreateBean.

doCreateBean篇幅較長,將拆分幾篇文章進行講解赵辕,如果覺得不錯既绩,請關(guān)注我的博客~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市还惠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌私杜,老刑警劉巖蚕键,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異衰粹,居然都是意外死亡锣光,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門铝耻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來誊爹,“玉大人,你說我怎么就攤上這事瓢捉∑登穑” “怎么了?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵泡态,是天一觀的道長搂漠。 經(jīng)常有香客問我,道長某弦,這世上最難降的妖魔是什么桐汤? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮靶壮,結(jié)果婚禮上怔毛,老公的妹妹穿的比我還像新娘。我一直安慰自己腾降,他們只是感情好拣度,可當(dāng)我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般蜡娶。 火紅的嫁衣襯著肌膚如雪混卵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天窖张,我揣著相機與錄音幕随,去河邊找鬼。 笑死宿接,一個胖子當(dāng)著我的面吹牛赘淮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播睦霎,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼梢卸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了副女?” 一聲冷哼從身側(cè)響起蛤高,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎碑幅,沒想到半個月后戴陡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡沟涨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年恤批,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片裹赴。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡喜庞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出棋返,到底是詐尸還是另有隱情延都,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布懊昨,位于F島的核電站窄潭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏酵颁。R本人自食惡果不足惜嫉你,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望躏惋。 院中可真熱鬧幽污,春花似錦、人聲如沸簿姨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至准潭,卻和暖如春趁俊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背刑然。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工寺擂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人泼掠。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓怔软,卻偏偏與公主長得像,于是被迫代替她去往敵國和親择镇。 傳聞我的和親對象是個殘疾皇子挡逼,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容