Spring bean實例化

問題一:何時生成代理對象卷玉,尤其是事務(wù)的代理對象?
在兩個地方可能生成代理對象:
一:InstantiationAwareBeanPostProcessor. postProcessBeforeInstantiation
二:BeanPostProcessor.postProcessAfterInitialization

生成事務(wù)的代理對象執(zhí)行點:
(1)在InfrastructureAdvisorAutoProxyCreator.postProcessAfterInitialization方法執(zhí)行
(2)SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference執(zhí)行

問題二:循環(huán)依賴解決?
為何要三級緩存倔既,而不是二級緩存询微?
好處:可擴展葫隙,修改提前暴露的對象
(把 aop 代理對象提前暴露出去株依,如果此時不暴露,循環(huán)依賴時延窜,A 引用的對象不是B 的代理對象)

實現(xiàn):三級緩存中存放的為ObjectFactory對象:singletonFactories恋腕,當(dāng)循環(huán)依賴時獲取緩存對象時,會執(zhí)行工廠方法
ObjectFactory.getObject()-------->
SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference
進而獲取AOP代理對象逆瑞。

實現(xiàn)過程

三級緩存.png

SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法作用:
當(dāng)存在循環(huán)依賴時荠藤,通過該回調(diào)方法獲取AOP代理對象
(正常是在InfrastructureAdvisorAutoProxyCreator.postProcessAfterInitialization方法中生成代理對象的)伙单。

具體過程

  1. 實例化對象后會放三級緩存
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    注意:放入緩存的是一個ObjectFactory實現(xiàn),只有在循環(huán)依賴時哈肖,第二個對象獲取第一個對象時吻育,會執(zhí)行ObjectFactory.getObject()方法實現(xiàn)(即getEarlyBeanReference)

獲取緩存

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

AbstractAutowireCapableBeanFactory#getEarlyBeanReference

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

AbstractAutoProxyCreator是SmartInstantiationAwareBeanPostProcessor的子類,
實現(xiàn)了getEarlyBeanReference用于生成 aop 代理對象的

@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    if (!this.earlyProxyReferences.contains(cacheKey)) {
        this.earlyProxyReferences.add(cacheKey);
    }
    return wrapIfNecessary(bean, beanName, cacheKey);
}

::wrapIfNecessary方法時生成 AOP代理對象::

  1. 正常生成 aop 代理對象是在postProcessAfterInitialization方法后淤井,
    AbstractAutoProxyCreator#postProcessAfterInitialization實現(xiàn)
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

::postProcessAfterInitialization 中也使用了 wrapIfNecessary::
::不過如果由于在賦值時布疼,為了解決循環(huán)依賴已經(jīng)生成過AOP代理對象,這時不再生成代理::

一文告訴你Spring是如何利用三級緩存巧妙解決Bean的循環(huán)依賴問題的
Spring源碼解析(十二)Spring擴展接口SmartInstantiationAwareBeanPostProcessor

BeanFactory

通過源代碼看看創(chuàng)建一個Bean實例的具體執(zhí)行流程:
[image:A568E489-2D7C-4E9B-9F0B-E1078E700F57-1306-000001E35047C75A/E453601C-514D-4406-9623-ACAB4F899BBD.png]

  1. bean的實例化通過BeanFactory接口實現(xiàn):DefaultListableBeanFactory,XmlBeanFactory,ApplicationContext 等具體的容器都是實現(xiàn)了BeanFactory币狠,再在其基礎(chǔ)之上附加了其他的功能游两。
  2. AbstractApplicationContext內(nèi)部使用DefaultListableBeanFactory,且DefaultListableBeanFactory繼承AbstractAutowireCapableBeanFactory漩绵,因此我們此處分析AbstractAutowireCapableBeanFactory即可贱案。
  3. AbstractAutowireCapableBeanFactory的方法populateBean是主要實現(xiàn),用于給bean實例填充數(shù)據(jù)

Spring創(chuàng)建Bean的流程

首先需要了解是Spring它創(chuàng)建Bean的流程止吐,我把它的大致調(diào)用棧繪圖如下:

Spring創(chuàng)建Bean的流程.png

對Bean的創(chuàng)建最為核心三個方法解釋如下:
createBeanInstance:實例化宝踪,其實也就是調(diào)用對象的構(gòu)造方法實例化對象
populateBean:填充屬性,這一步主要是對bean的依賴屬性進行注入(@Autowired)
initializeBean:回到一些形如initMethod碍扔、InitializingBean等方法
一文告訴你Spring是如何利用三級緩存巧妙解決Bean的循環(huán)依賴問題

BeanFactory中bean的生命周期

當(dāng)請求getBean時瘩燥,即實例化bean對象。ApplicationContext是在refresh()方法里立即實例化了所有的bean蕴忆。

BeanFactory中bean的生命周期.png

主要分為三階段

  1. 實例化颤芬,創(chuàng)建Bean的過程,InstantiationAwareBeanPostProcessor作用于此
  2. 填充數(shù)據(jù)
  3. 初始化套鹅,BeanPostProcessor作用于此

階段一:實例化

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()
此方法為核心方法站蝠,用于創(chuàng)建bean實例,填充bean屬性卓鹿,并執(zhí)行后置方法

/**
 * 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, @Nullable Object[] args)
        throws BeanCreationException {
    RootBeanDefinition mbdToUse = mbd;

  //此處為特殊邏輯菱魔,如果有InstantiationAwareBeanPostProcessor實現(xiàn)生成代理對象,則直接返回吟孙,不會調(diào)下面的方法
    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;
        }
    }
    //創(chuàng)建bean實例澜倦,調(diào)用populateBean填充屬性
    try {
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        if (logger.isDebugEnabled()) {
            logger.debug("Finished creating instance of bean '" + beanName + "'");
        }
        return beanInstance;
    }
}

resolveBeforeInstantiation 的功能為:
如果有InstantiationAwareBeanPostProcessor實現(xiàn)的postProcessBeforeInstantiation方法生成代理對象,則直接返回杰妓,不會調(diào)下面的方法

@Nullable
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;
}

注意:實例化之前執(zhí)行InstantiationAwareBeanPostProcessor. postProcessBeforeInstantiation方法藻治,當(dāng)經(jīng)過前置處理之后,返回的結(jié)果若不為空巷挥,那么會直接略過后續(xù)的Bean的創(chuàng)建而直接返回結(jié)果
::原因:resolveBeforeInstantiation只是針對有自定義的targetsource桩卵,因為自定義的targetsource不是spring的bean那么肯定不需要進行后續(xù)的一系列的實例化初始化。所以可以在resolveBeforeInstantiation直接進行proxy::

Spring源碼解析(十一)Spring擴展接口InstantiationAwareBeanPostProcessor解析
Spring之InstantiationAwareBeanPostProcessor接口介紹 - 云+社區(qū) - 騰訊云
Spring Aop之Target Source詳解Java愛寶貝丶的博客-CSDN博客

階段二:填充實例-populateBean實現(xiàn)

位于:AbstractAutowireCapableBeanFactory#populateBean

主要功能:

  1. 把Autowired注解的屬性注入到實例中
  2. 執(zhí)行實現(xiàn)了InstantiationAwareBeanPostProcessor接口的postProcessAfterInstantiation方法修改實例

執(zhí)行流程。如下:

  • 獲取屬性列表 pvs
  • 在屬性被填充到 bean 前雏节,應(yīng)用后置處理自定義屬性填充
  • 根據(jù)名稱或類型解析相關(guān)依賴
  • 再次應(yīng)用后置處理胜嗓,用于動態(tài)修改屬性列表 pvs 的內(nèi)容
  • 將屬性應(yīng)用到 bean 對象中
    注意第3步,也就是根據(jù)名稱或類型解析相關(guān)依賴(autowire)钩乍。該邏輯只會解析依賴辞州,并不會將解析出的依賴立即注入到 bean 對象中。所有的屬性值是在 applyPropertyValues 方法中統(tǒng)一被注入到 bean 對象中的寥粹。
    Spring IOC 容器源碼分析 - 填充屬性到 bean 原始對象_慕課手記
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
    // 獲取屬性列表
    PropertyValues pvs = mbd.getPropertyValues();

    if (bw == null) {
        if (!pvs.isEmpty()) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
        }
        else {
            return;
        }
    }

    boolean continueWithPropertyPopulation = true;
    /*
     * 在屬性被填充前变过,給 InstantiationAwareBeanPostProcessor 類型的后置處理器一個修改 
     * bean 狀態(tài)的機會。關(guān)于這段后置引用排作,官方的解釋是:讓用戶可以自定義屬性注入牵啦。比如用戶實現(xiàn)一
     * 個 InstantiationAwareBeanPostProcessor 類型的后置處理器,并通過 
     * postProcessAfterInstantiation 方法向 bean 的成員變量注入自定義的信息妄痪。當(dāng)然衫生,如果無
     * 特殊需求,直接使用配置中的信息注入即可。另外,Spring 并不建議大家直接實現(xiàn) 
     * InstantiationAwareBeanPostProcessor 接口,如果想實現(xiàn)這種類型的后置處理器帕膜,更建議
     * 通過繼承 InstantiationAwareBeanPostProcessorAdapter 抽象類實現(xiàn)自定義后置處理器。
     */
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                    continueWithPropertyPopulation = false;
                    break;
                }
            }
        }
    }

    /* 
     * 如果上面設(shè)置 continueWithPropertyPopulation = false,表明用戶可能已經(jīng)自己填充了
     * bean 的屬性瞒大,不需要 Spring 幫忙填充了。此時直接返回即可
     */
    if (!continueWithPropertyPopulation) {
        return;
    }

    // 根據(jù)名稱或類型注入依賴
    if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
            mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

        // 通過屬性名稱注入依賴
        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
            autowireByName(beanName, mbd, bw, newPvs);
        }

        // 通過屬性類型注入依賴
        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
            autowireByType(beanName, mbd, bw, newPvs);
        }

        pvs = newPvs;
    }

    boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
    boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);

    /*
     * 這里又是一種后置處理背率,用于在 Spring 填充屬性到 bean 對象前划滋,對屬性的值進行相應(yīng)的處理,
     * 比如可以修改某些屬性的值。這時注入到 bean 中的值就不是配置文件中的內(nèi)容了,
     * 而是經(jīng)過后置處理器修改后的內(nèi)容
     */ 
    if (hasInstAwareBpps || needsDepCheck) {
        PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
        if (hasInstAwareBpps) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    // 對屬性進行后置處理
                    pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                    if (pvs == null) {
                        return;
                    }
                }
            }
        }
        if (needsDepCheck) {
            checkDependencies(beanName, mbd, filteredPds, pvs);
        }
    }

    // 應(yīng)用屬性值到 bean 對象中
    applyPropertyValues(beanName, mbd, bw, pvs);

階段三:初始化實例

初始化過程主要包含以下幾個過程:

  1. 執(zhí)行BeanNameAware, BeanClassLoaderAware, BeanFactoryAware 接口實現(xiàn)
  2. 執(zhí)行BeanPostProcessor.postProcessAfterInitialization:
  3. 執(zhí)行InitializingBean接口的中afterPropertiesSet()方法(如果該實例實現(xiàn))
  4. 執(zhí)行實例init method
  5. 執(zhí)行BeanPostProcessor.postProcessAfterInitialization

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {

  invokeAwareMethods(beanName, bean);
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null),
                beanName, "Invocation of init method failed", ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末服傍,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子骂铁,更是在濱河造成了極大的恐慌吹零,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拉庵,死亡現(xiàn)場離奇詭異灿椅,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門茫蛹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來操刀,“玉大人,你說我怎么就攤上這事婴洼」强樱” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵柬采,是天一觀的道長欢唾。 經(jīng)常有香客問我,道長粉捻,這世上最難降的妖魔是什么礁遣? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮肩刃,結(jié)果婚禮上祟霍,老公的妹妹穿的比我還像新娘。我一直安慰自己盈包,他們只是感情好浅碾,可當(dāng)我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著续语,像睡著了一般垂谢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上疮茄,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天滥朱,我揣著相機與錄音,去河邊找鬼力试。 笑死徙邻,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的畸裳。 我是一名探鬼主播缰犁,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼怖糊!你這毒婦竟也來了帅容?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤伍伤,失蹤者是張志新(化名)和其女友劉穎并徘,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扰魂,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡麦乞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年蕴茴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姐直。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡倦淀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出声畏,到底是詐尸還是另有隱情晃听,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布砰识,位于F島的核電站,受9級特大地震影響佣渴,放射性物質(zhì)發(fā)生泄漏辫狼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一辛润、第九天 我趴在偏房一處隱蔽的房頂上張望膨处。 院中可真熱鬧,春花似錦砂竖、人聲如沸真椿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽突硝。三九已至,卻和暖如春置济,著一層夾襖步出監(jiān)牢的瞬間解恰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工浙于, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留护盈,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓羞酗,卻偏偏與公主長得像腐宋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子檀轨,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,901評論 2 355