Spring-IOC-SingletonBean的創(chuàng)建過程

如果緩存中沒有單例Bean的緩存,則需要從頭開始創(chuàng)建單例Bean怠缸,這主要是重載getSingleton的重載方法來實現(xiàn)單例Bean的加載钳宪。

getSingleton方法

3. 獲取單例
    /**
     * Return the (raw) singleton object registered under the given name,
     * creating and registering a new one if none registered yet.
     * @param beanName the name of the bean
     * @param singletonFactory the ObjectFactory to lazily create the singleton
     * with, if necessary
     * @return the registered singleton object
     */
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "'beanName' 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<Exception>();
                }
                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 != NULL_OBJECT ? singletonObject : null);
        }
    }

前后處理方法:

3.1 
    /**
     * Callback before singleton creation.
     * <p>The default implementation register the singleton as currently in creation.
     * @param beanName the name of the singleton about to be created
     * @see #isSingletonCurrentlyInCreation
     */
    protected void beforeSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    }
3.2 
    /**
         * Callback after singleton creation.
         * <p>The default implementation marks the singleton as not in creation anymore.
         * @param beanName the name of the singleton that has been created
         * @see #isSingletonCurrentlyInCreation
         */
    protected void afterSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
            throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
        }
    }
3.3 
    /**
     * Add the given singleton object to the singleton cache of this factory.
     * <p>To be called for eager registration of singletons.
     * @param beanName the name of the bean
     * @param singletonObject the singleton object
     */
    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
3.4 從3返回
sharedInstances = getSingleton(String beanName, ObjectFactory<?> singletonFactory)

這個方法只是做一些準備以及處理操作半醉,而真正的獲取單例bean的方法其實并不是在此方法中實現(xiàn)的,其實現(xiàn)邏輯是在ObjectFactory類型的實例singletonFactory中實現(xiàn)的缩多。

準備及處理操作如下:

  1. 首先獲取SingletonObjects的對象鎖衬吆,保證單例的全局唯一性。

  2. 檢查緩存是否已經(jīng)加載過姆泻。

  3. 若沒有加載冒嫡,則記錄beanName的正在加載狀態(tài)

  4. 加載單例前記錄加載狀態(tài)

    beforeSingletonCreation()方法用于記錄加載的狀態(tài):
    調(diào)用this.singletonsCurrentlyInCreation.add(beanName)將當前正要創(chuàng)建的bean記錄在緩存中孝凌,這樣便可以對循環(huán)依賴進行檢測啦

  5. 通過調(diào)用參數(shù)傳入的ObjectFactory的getObject方法實例化bean

    singletonObject = singletonFactory.getObject();

  6. 加載單例后的處理方法調(diào)用

    當bean加載結(jié)束后需要移除緩存中對該bean的正在加載狀態(tài)的記錄: afterSingletonCreation()方法調(diào)用

  7. 將結(jié)果記錄至緩存并刪除加載bean過程中所有記錄的各種輔助狀態(tài)

    調(diào)用addSingleton()方法

  8. 返回處理結(jié)果

    上述是加載bean的邏輯框架,現(xiàn)在為止還沒有對bean加載功能的探索峻呛,其實bean的加載邏輯是在傳入的ObjectFactory類型的參數(shù)singletonFactory中定義的辜窑,而ObjectFactory的核心代碼只是調(diào)用了createBean的方法,接下來就去探究一下這個方法的奧妙牙勘。

createBean方法詳解

準備創(chuàng)建bean(createBean方法詳解)
beans.factory.support.AbstractAutowireCapableBeanFactory
    //---------------------------------------------------------------------
    // Implementation of relevant AbstractBeanFactory template methods
    //---------------------------------------------------------------------

    /**
     * Central method of this class: creates a bean instance,
     * populates the bean instance, applies post-processors, etc.
     * @see #doCreateBean
     */
    @Override
    protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating instance of bean '" + beanName + "'");
        }
        RootBeanDefinition mbdToUse = mbd;

        // Make sure bean class is actually resolved at this point, and
        // clone the bean definition in case of a dynamically resolved Class
        // which cannot be stored in the shared merged bean definition.
        Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
        if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
            mbdToUse = new RootBeanDefinition(mbd);
            mbdToUse.setBeanClass(resolvedClass);
        }

        // Prepare method overrides.
        try {
            mbdToUse.prepareMethodOverrides();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
                    beanName, "Validation of method overrides failed", ex);
        }

        try {
            // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
            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);
        }

        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        if (logger.isDebugEnabled()) {
            logger.debug("Finished creating instance of bean '" + beanName + "'");
        }
        return beanInstance;
    }

createBean方法主要步驟:

  1. 根據(jù)設置的class屬性或者根據(jù)className來解析Class。

  2. 對override屬性進行標記以及驗證(是針對Spring配置中l(wèi)ookup-method色徘、replace-method bean的子標簽進行處理褂策,因為這兩個子標簽的加載過程就是將配置統(tǒng)一存放在BeanDefinition中的methodOverride屬性里)。

  3. 應用實例前的后處理器耿焊,給后處理器一個返回代理而不是目標bean實例的機會遍搞,如果返回的代理類不為空則直接返回,而不會進行下面的創(chuàng)建bean的過程钩杰。

  4. 創(chuàng)建bean(doCreateBean方法)诊县。這又是一個比較重要而且復雜的過程,需要仔細分析垂睬。

下面重點講解步驟2抗悍、3、4赏壹。

步驟2- 處理override屬性

用于處理配置中的lookup-method以及replace-method屬性衔沼。

主要完成的工作:

  1. 標記

如果一個類中存在若干個重載方法,方法調(diào)用以及增強的時候還需要根據(jù)參數(shù)類型進行匹配菩佑,來最終確定當前調(diào)用的是哪個方法稍坯,Spring在這里將匹配的工作在這里完成匹配的,這樣在后續(xù)調(diào)用的時候變可以直接是我用找到的方法瞧哟,而不需要進行方法的參數(shù)匹配驗證了

  1. 驗證

在標記的過程中可以對方法存在性進行驗證勤揩,一箭雙雕。

4.1 處理override屬性
beans.factory.support.AbstractBeanFactory(XmlBeanFactory 繼承自這個類凿傅,擁有這個方法)
    /**
     * Validate and prepare the method overrides defined for this bean.
     * Checks for existence of a method with the specified name.
     * @throws BeanDefinitionValidationException in case of validation failure
     */
    public void prepareMethodOverrides() throws BeanDefinitionValidationException {
        // Check that lookup methods exists.
        MethodOverrides methodOverrides = getMethodOverrides();
        if (!methodOverrides.isEmpty()) {
            Set<MethodOverride> overrides = methodOverrides.getOverrides();
            synchronized (overrides) {
                for (MethodOverride mo : overrides) {
                    prepareMethodOverride(mo);
                }
            }
        }
    }
    /**
     * Validate and prepare the given method override.
     * Checks for existence of a method with the specified name,
     * marking it as not overloaded if none found.
     * @param mo the MethodOverride object to validate
     * @throws BeanDefinitionValidationException in case of validation failure
     */
    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() + "]");
        }
        else if (count == 1) {
            // Mark override as not overloaded, to avoid the overhead of arg type checking.
            mo.setOverloaded(false);
        }
    }

步驟3- 實例化前的的后處理

doCreateBean()方法之前調(diào)用了resolveBeforeInstantiation方法對BeanDefinition中的屬性進行前置處理聪舒。

  1. 調(diào)用實例化前處理器進行處理虐急。

  2. 短路判斷:如果前置處理返回的結(jié)果不為空止吁,則直接略過后續(xù)Bean的創(chuàng)建而直接返回結(jié)果,這一特性至關重要敬惦,AOP功能就是基于這里的判斷的俄删,如果不為空則調(diào)用初始化后處理器。

實例化的前置處理
beans.factory.support.AbstractAutowireCapableBeanFactory
    /**
     * Apply before-instantiation post-processors, resolving whether there is a
     * before-instantiation shortcut for the specified bean.
     * @param beanName the name of the bean
     * @param mbd the bean definition for the bean
     * @return the shortcut-determined bean instance, or {@code null} if none
     */
    protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
        Object bean = null;
        if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
            // Make sure bean class is actually resolved at this point.
            if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                Class<?> targetType = determineTargetType(beanName, mbd);
                if (targetType != null) {
                    bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                    if (bean != null) {
                        bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                    }
                }
            }
            mbd.beforeInstantiationResolved = (bean != null);
        }
        return bean;
    }

4.2.1 初始化前的后處理器
    /**
     * Apply InstantiationAwareBeanPostProcessors to the specified bean definition
     * (by class and name), invoking their {@code postProcessBeforeInstantiation} methods.
     * <p>Any returned object will be used as the bean instead of actually instantiating
     * the target bean. A {@code null} return value from the post-processor will
     * result in the target bean being instantiated.
     * @param beanClass the class of the bean to be instantiated
     * @param beanName the name of the bean
     * @return the bean object to use instead of a default instance of the target bean, or {@code null}
     * @see InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
     */
    protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
                if (result != null) {
                    return result;
                }
            }
        }
        return null;
    }
4.2.2 實例化后的后處理器
    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

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

實例化前的后處理器的應用

將AbstractBeanDefinition轉(zhuǎn)換為BeanWrapper前的處理斜脂,給子類一個修改BeanDefinition的機會,也就是說當程序經(jīng)過這個方法之后玷或,bean可能已經(jīng)不是我們認為的bean了,而是或許成為一個經(jīng)過處理的代理bean偏友,可能是通過cglib生成的,也可能是通過其他技術生成的,會在AOP中進行講解棱诱,需要清楚的是在bean的實例化前會調(diào)用后處理器的方法進行處理涝动。

初始化后的后處理器

Spring保證bean初始化后盡可能將注冊的后處理器postProcessAfterInitialization方法應用到該bean中。

步驟4- doCreateBean方法詳解

Spring-IOC-循環(huán)依賴檢測與Bean的創(chuàng)建

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市米愿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌较鼓,老刑警劉巖违柏,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件漱竖,死亡現(xiàn)場離奇詭異,居然都是意外死亡躺率,警方通過查閱死者的電腦和手機万矾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門勤众,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人们颜,你說我怎么就攤上這事猎醇×蛩唬” “怎么了梧税?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長哮塞。 經(jīng)常有香客問我凳谦,道長,這世上最難降的妖魔是什么家凯? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任绊诲,我火速辦了婚禮褪贵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘板惑。我一直安慰自己偎快,他們只是感情好,可當我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布裆馒。 她就那樣靜靜地躺著丐怯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪梗搅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天荡短,我揣著相機與錄音掘托,去河邊找鬼籍嘹。 笑死,一個胖子當著我的面吹牛泪掀,可吹牛的內(nèi)容都是我干的识补。 我是一名探鬼主播辫红,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼贴妻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了名惩?” 一聲冷哼從身側(cè)響起娩鹉,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎戚宦,沒想到半個月后锈嫩,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡艳汽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年河狐,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片义郑。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡丈钙,死狀恐怖雏赦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情星岗,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站寥掐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏百炬。R本人自食惡果不足惜污它,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一衫贬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧固惯,春花似錦缝呕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春色迂,著一層夾襖步出監(jiān)牢的瞬間手销,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工诈悍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留兽埃,地道東北人柄错。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像给猾,于是被迫代替她去往敵國和親趁矾。 傳聞我的和親對象是個殘疾皇子给僵,可洞房花燭夜當晚...
    茶點故事閱讀 45,077評論 2 355

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理帝际,服務發(fā)現(xiàn),斷路器斑粱,智...
    卡卡羅2017閱讀 134,672評論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,823評論 6 342
  • Spring容器高層視圖 Spring 啟動時讀取應用程序提供的Bean配置信息则北,并在Spring容器中生成一份相...
    Theriseof閱讀 2,817評論 1 24
  • 我的第一份工作很不如意。 換工作時,我準備了6個月的生活費名秀,打算長期抗戰(zhàn)藕溅。但沒料到,投完簡歷不到一星期耗跛,就被叫去面...
    越女事務所閱讀 200評論 0 0
  • 前兩天的豆瓣評分報告出來了攒发,我的2015年標記了200+的電影(雖然有些事很久之前看的,隨手記上了) 發(fā)現(xiàn)了自己看...
    獨行向日葵閱讀 207評論 1 0