spring之Bean實例化過程

一坤塞、概述

spring初始化Bean的流程比較復雜,除了普通的構造函數(shù)實例化Bean之外澈蚌,spring還支持工廠模式創(chuàng)建Bean(即常說的Factory Bean)摹芙。在spring實例化完Bean之后,會進行Bean的一些初始化工作宛瞄。其中涉及到擴展點調(diào)用浮禾、運行時類型成員解析、依賴注入份汗、懶加載等流程盈电,還解決了循環(huán)引用、類型轉(zhuǎn)換等問題裸影。熟悉Bean加載的過程挣轨,開發(fā)過程中選擇擴展點時就可以做到游刃有余。為了更加關注邏輯流程轩猩,本文所貼的代碼精簡掉了不相關的部分卷扮。

二荡澎、Bean實例化概要流程
1.獲取Bean過程中涉及到的擴展點

org.springframework.beans.factory.support.MethodOverride
org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor
org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor
org.springframework.beans.factory.config.BeanPostProcessor
org.springframework.beans.factory.InitializingBean

2.關鍵方法框架圖
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean

org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean
三、Bean實例化詳細流程
1.相關入口
  • 根據(jù)類型獲取Bean
    org.springframework.context.support.AbstractApplicationContext.getBean(Class<T>)
  • 根據(jù)名字獲取Bean
    org.springframework.context.support.AbstractApplicationContext.getBean(String)
  • 實際獲取Bean
    org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean
  • 根據(jù)給出的Bean獲取真實Bean(可能是factoryBean)
    org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance
  • 創(chuàng)建Bean
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean
  • 實際創(chuàng)建Bean
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean
2.詳細解析

除了單例且非懶加載的Bean是在ApplicationContext刷新的時候初始化之外晤锹。其他Bean都是在使用的時候才會加載摩幔,即我們常用的getBean方法。這個方法提供了很多重載鞭铆,總的類型只有兩種:

  • 根據(jù)類型獲取Bean
  • 根據(jù)Name獲取Bean
    兩種類型最終內(nèi)部都調(diào)用到了同一個方法或衡,詳見下述偽代碼:
-------------------- 根據(jù)類型獲取Bean最終調(diào)用的是doGetBean方法
-- org.springframework.context.support.AbstractApplicationContext
public <T> T getBean(Class<T> requiredType) throws BeansException {
    return getBeanFactory().getBean(requiredType);
}
-- org.springframework.beans.factory.support.DefaultListableBeanFactory
public <T> T getBean(Class<T> requiredType) throws BeansException {
    return getBean(requiredType, (Object[]) null);
}
public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
    String[] beanNames = getBeanNamesForType(requiredType);
    if (beanNames.length == 1) {
        return getBean(beanNames[0], requiredType, args);
    }
}
-- org.springframework.beans.factory.support.AbstractBeanFactory.getBean
public <T> T getBean(String name, Class<T> requiredType, Object[] args) throws BeansException {
  return doGetBean(name, requiredType, args, false);
}

-------------------- 根據(jù)名字獲取Bean最終調(diào)用的也是doGetBean方法
-- org.springframework.context.support.AbstractApplicationContext
public Object getBean(String name) throws BeansException {
    return getBeanFactory().getBean(name);
}

-- org.springframework.beans.factory.support.AbstractBeanFactory
public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}

doGetBean方法中很多地方都會調(diào)用getObjectForBeanInstance來獲取最終需要的bean。該方法是spring為實現(xiàn)FactoryBean而編寫的车遂,其內(nèi)部判斷了是否需要利用工廠模式來創(chuàng)建出目標bean封断,詳細偽代碼如下:

protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
    -- 如果對象不是一個FactoryBean或者這里就是要取FactoryBean,則直接返回
    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
        return beanInstance;
    }
    -- 如果對象是一個FactoryBean且這里要獲取對應的Bean舶担,則嘗試從factoryBean已創(chuàng)建的對象緩存中獲取
    Object object = null;
    if (mbd == null) {
        object = getCachedObjectForFactoryBean(beanName);
    }
    -- 緩存中沒有則用Factory去創(chuàng)建出Bean
    if (object == null) {
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        if (mbd == null && containsBeanDefinition(beanName)) {
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        boolean synthetic = (mbd != null && mbd.isSynthetic());
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}
-- org.springframework.beans.factory.support.FactoryBeanRegistrySupport
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    Object object = doGetObjectFromFactoryBean(factory, beanName);
    object = postProcessObjectFromFactoryBean(object, beanName);
    this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
    return (object != NULL_OBJECT ? object : null);
}

-- doGetObjectFromFactoryBean中關鍵代碼
object = factory.getObject();
return object;

如果factory緩存中沒有需要的Bean時坡疼,不管是單例Bean、模板Bean衣陶,最終都會調(diào)用createBean方法來創(chuàng)建一個Bean柄瑰,并調(diào)用getObjectForBeanInstance方法確定目標bean。createBean內(nèi)部會調(diào)用doCreateBean方法進行對象的創(chuàng)建剪况、初始化等工作教沾,詳細偽代碼如下:

-- doGetBean
  Object bean = createBean(beanName, mbd, args);
  bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
-- org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
    resolveBeanClass(mbd, beanName);
    mbd.prepareMethodOverrides();
    Object bean = resolveBeforeInstantiation(beanName, mbd);
    if (bean != null) {
        return bean;
    }
    Object beanInstance = doCreateBean(beanName, mbd, args);
    return beanInstance;
}
-- org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
    // 創(chuàng)建目標對象,包裝成BeanWrapper
    BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
    // 執(zhí)行MergedBeanDefinitionPostProcessor接口
    applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
    //將單例添加到Bean工廠的緩存中译断,解決循環(huán)引用問題
    ...
    // 成員變量初始化
    populateBean(beanName, mbd, instanceWrapper);
    Object exposedObject =  initializeBean(beanName, bean, mbd);
    //實例化它依賴的Bean
    ...
    //如果這個Bean是可銷毀的Bean授翻,則注冊到factory中。
    registerDisposableBeanIfNecessary(beanName, bean, mbd);
    return exposedObject;
}

populateBean和initializeBean完成了Bean的成員變量初始化工作镐作。其中藏姐,populateBean方法在屬性設置之前調(diào)用了InstantiationAwareBeanPostProcessor隆箩,允許實現(xiàn)自定義方式的屬性注入该贾。之后把依賴的非簡單類型取出依賴注入(有set方法的這種)。再把所有屬性在類型轉(zhuǎn)換器走一次捌臊,設置到Bean中(applyPropertyValues)杨蛋。

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
    PropertyValues pvs = mbd.getPropertyValues();
    //調(diào)用InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation方法,
    ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)
    if (...) { //允許InstantiationAwareBeanPostProcessor終止初始化流程
        return;
    }
    //根據(jù)類型或者名字調(diào)用setXXX方法自動注入
    autowireByXXX(beanName, mbd, bw, newPvs);
    //成員被自動注入后理澎,調(diào)用InstantiationAwareBeanPostProcessor的postProcessPropertyValues方法
    pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
    if (pvs == null) {
        return;
    }
    //解析所有運行時的引用逞力,應用所有的屬性值(包含類型轉(zhuǎn)換)。
    applyPropertyValues(beanName, mbd, bw, pvs);
}

initializeBean調(diào)用了些Aware方法:

  • BeanNameAware
  • BeanClassLoaderAware
  • BeanFactoryAware
    調(diào)用BeanPostProcessor的postProcessBeforeInitialization方法(例如ApplicationContextAware)糠爬。如果Bean實現(xiàn)了InitializingBean寇荧,則調(diào)用它的afterPropertiesSet方法。調(diào)用Bean設置的initMethod方法执隧,調(diào)用BeanPostProcessor的postProcessAfterInitialization方法揩抡。詳細的偽代碼如下:
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
    invokeAwareMethods(beanName, bean);
    Object wrappedBean = bean;
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    //包含InitializingBean的方法和自定義的init方法
    invokeInitMethods(beanName, wrappedBean, mbd);
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    return wrappedBean;
}
四户侥、總結

spring實例化Bean過程,提供了許多擴展點峦嗤。其Factory Bean模式也是通過getBean方法內(nèi)部做了特殊處理才實現(xiàn)的蕊唐。在Bean被實例化之前,會調(diào)用MergedBeanDefinitionPostProcessor進行預處理烁设,給予了我們對對象進行代理的機會替梨,使其不去創(chuàng)建具體的目標類型對象。在配置文件加載前装黑、加載后副瀑,spring都提供了可供我們添加邏輯的接口,來實現(xiàn)自己的增強功能恋谭。
對ApplicationContext初始化流程不熟悉的話俗扇,可以參考這篇文章:spring擴展之a(chǎn)pplication context初始化流程

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市箕别,隨后出現(xiàn)的幾起案子铜幽,更是在濱河造成了極大的恐慌,老刑警劉巖串稀,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件除抛,死亡現(xiàn)場離奇詭異,居然都是意外死亡母截,警方通過查閱死者的電腦和手機到忽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來清寇,“玉大人喘漏,你說我怎么就攤上這事』蹋” “怎么了翩迈?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長盔夜。 經(jīng)常有香客問我负饲,道長,這世上最難降的妖魔是什么喂链? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任返十,我火速辦了婚禮,結果婚禮上椭微,老公的妹妹穿的比我還像新娘洞坑。我一直安慰自己,他們只是感情好蝇率,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布迟杂。 她就那樣靜靜地躺著匈仗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪逢慌。 梳的紋絲不亂的頭發(fā)上悠轩,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音攻泼,去河邊找鬼火架。 笑死,一個胖子當著我的面吹牛忙菠,可吹牛的內(nèi)容都是我干的何鸡。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼牛欢,長吁一口氣:“原來是場噩夢啊……” “哼骡男!你這毒婦竟也來了?” 一聲冷哼從身側響起傍睹,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤隔盛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后拾稳,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吮炕,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年访得,在試婚紗的時候發(fā)現(xiàn)自己被綠了龙亲。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡悍抑,死狀恐怖鳄炉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情搜骡,我是刑警寧澤拂盯,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站浆兰,受9級特大地震影響磕仅,放射性物質(zhì)發(fā)生泄漏珊豹。R本人自食惡果不足惜簸呈,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望店茶。 院中可真熱鬧蜕便,春花似錦、人聲如沸贩幻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至族壳,卻和暖如春憔辫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背仿荆。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工贰您, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拢操。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓锦亦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親令境。 傳聞我的和親對象是個殘疾皇子杠园,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345