Spring核心源碼深度解析(六)實例化和初始化對象

?Spring 創(chuàng)建對象

在上一章我們對invokeBeanFactoryPostProcessors的重要部分進行了詳細的介紹猪腕,總算到了我們的Spring創(chuàng)建對象的過程了,本章會接著前面的refresh進行續(xù)寫。

registerBeanPostProcessors(beanFactory)

這個方法的特性和invokeBeanFactoryPostProcessors(beanFactory);其實差不太多,也是對后置處理器的一些操作潮峦,但這個方法的重量缺少了很多,相信讀者有了invokeBeanFactoryPostProcessors(beanFactory);的經(jīng)驗應該能夠自行理解其基本意義勇婴。

initMessageSource();

初始化國際化資源忱嘹,這里只是對一些資源加載的方式進行規(guī)范。

initApplicationEventMulticaster();

創(chuàng)建ApplicationEventMulticaster這么一個類耕渴,這個類是事件操控的核心拘悦,我們先跳過,這里不會影響整體Bean初始化

onRefresh();

空方法

registerListeners

注冊監(jiān)聽器

finishBeanFactoryInitialization(beanFactory)

總算到我們真正創(chuàng)建Bean的核心方法了橱脸,通過這個方法的名字就可以猜到础米,這就是我們Spring用來創(chuàng)建對象的核心方法,如果不相信的話我們在這里調(diào)試一下添诉,首先看第一張圖


image

singletonObjects就是我們的實例對象屁桑,這里不同的Spring版本可能singletonObjects所在的類會有一些區(qū)別,那么如果singletonObjects數(shù)量發(fā)生改變說創(chuàng)建對象就是在這里實現(xiàn)的栏赴,我們調(diào)試一下


image
image

可以看到singletonObjects的數(shù)量確實增加了幾個蘑斧,而且這幾個就是我們所創(chuàng)建的,那么我們就開始對其進行分析须眷,在對finishBeanFactoryInitialization深度分析時竖瘾,筆者只會對一些核心點進行解釋說明,因為內(nèi)容確實太多柒爸,有些比較特殊的我就一筆帶過准浴,話不多說看代碼

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // 這里不知道CONVERSION_SERVICE_BEAN_NAME有什么用
    if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
        beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
      beanFactory.setConversionService(
          beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
    }
?
    // Register a default embedded value resolver if no bean post-processor
    // (such as a PropertyPlaceholderConfigurer bean) registered any before:
    // at this point, primarily for resolution in annotation attribute values.
    //檢查Spring中是否存在類型轉(zhuǎn)換
    if (!beanFactory.hasEmbeddedValueResolver()) {
      beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
    }
    // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
    String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
    for (String weaverAwareName : weaverAwareNames) {
      getBean(weaverAwareName);
    }
?
    // 禁止使用臨時類加載器進行類型匹配
    beanFactory.setTempClassLoader(null);
?
    // Allow for caching all bean definition metadata, not expecting further changes.
    //允許緩存所有的bean的定義數(shù)據(jù)
    beanFactory.freezeConfiguration();
?
    // Instantiate all remaining (non-lazy-init) singletons.
    //重點,準備bean實例
    beanFactory.preInstantiateSingletons();
  }

注釋比較詳細捎稚,這里其實沒做什么事情乐横,核心方法是beanFactory.preInstantiateSingletons();我們點進去

  public void preInstantiateSingletons() throws BeansException {
    if (logger.isTraceEnabled()) {
      logger.trace("Pre-instantiating singletons in " + this);
    }
    // Iterate over a copy to allow for init methods which in turn register new bean definitions.
    // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
    //拿到所有的bd名字
    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
?
    // Trigger initialization of all non-lazy singleton beans...
    //循環(huán)并判斷是不是不是懶加載的求橄,是不是FactoryBean,然后對bd進行bean創(chuàng)建
    for (String beanName : beanNames) {
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
        if (isFactoryBean(beanName)) {
          Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
          if (bean instanceof FactoryBean) {
            final FactoryBean<?> factory = (FactoryBean<?>) bean;
            boolean isEagerInit;
            if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
              isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
                      ((SmartFactoryBean<?>) factory)::isEagerInit,
                  getAccessControlContext());
            }
            else {
              isEagerInit = (factory instanceof SmartFactoryBean &&
                  ((SmartFactoryBean<?>) factory).isEagerInit());
            }
            if (isEagerInit) {
              getBean(beanName);
            }
          }
        }
        else {
          getBean(beanName);
        }
      }
    }
?
    // Trigger post-initialization callback for all applicable beans...
    for (String beanName : beanNames) {
      Object singletonInstance = getSingleton(beanName);
      if (singletonInstance instanceof SmartInitializingSingleton) {
        final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
        if (System.getSecurityManager() != null) {
          AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            smartSingleton.afterSingletonsInstantiated();
            return null;
          }, getAccessControlContext());
        }
        else {
          smartSingleton.afterSingletonsInstantiated();
        }
      }
    }
  }

這里首先執(zhí)行RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);將我們的BeanDefinition合并葡公,最后都采用RootBeanDefinition聲明罐农,然后判斷是不是單例對象,是不是FactoryBean,如果不是就會進入getBean(beanName);我們在跟進


image

這里為什么是GetBean而不是創(chuàng)建對象催什,其實 Spring在很多地方都會獲取對象涵亏,所以不可能每次獲取都是創(chuàng)建對象,而應該是先查詢有沒有蒲凶,再創(chuàng)建就像我們使用的緩存技術一樣气筋。我們繼續(xù)


image

進入這句代碼:Object sharedInstance = getSingleton(beanName);


image

這里先簡單分析下,如果獲取的對象是沒有完全創(chuàng)建的旋圆,那么的singletonObject為空宠默,isSingletonCurrentlyInCreation(beanName)也會為false那么判斷會退出,記住這個isSingletonCurrentlyInCreation灵巧,然后我們繼續(xù)


image

這些都不重要搀矫,我們往下看----

Bean實例化

image

這里的return createBean(beanName, mbd, args);是表達式語法,其實就是一個回調(diào)方式刻肄,然后我們調(diào)試進入getSingleton瓤球,


image

這里會做一些判斷,如果沒有實例化這個對象那么
它就會執(zhí)行這段代碼beforeSingletonCreation(beanName);我們看看這段代碼做了什么


image

他會將我們的對象標識為正在創(chuàng)建敏弃,記住這個標識


image

然后再往下走回走到singletonObject = singletonFactory.getObject();


image

可以看到這里產(chǎn)生回調(diào)卦羡,不熟悉函數(shù)式的朋友們可以先忽略它,我們進入createBean(beanName, mbd, args);


image
image

前面都不重要权她,可以看到創(chuàng)建對象的代碼在這一行Object beanInstance =

doCreateBean(beanName, mbdToUse, args);筆者這里再啰嗦一下這段代碼Object bean =resolveBeforeInstantiation(beanName, mbdToUse);
image

這里筆者認為設計的有些累贅了虹茶,它的目的其實就是如果程序員定義的對象實現(xiàn)這個InstantiationAwareBeanPostProcessor接口,那么Spring就會認為這個對象不需要經(jīng)過Spring的完整生命周期隅要,需要脫離Spring管理的意思蝴罪,然后直接return掉,但我想Spring可能也是為了擴展的開放性吧步清,畢竟Spring的設計考慮的十分周全要门。好了,我們回到代碼Object beanInstance = doCreateBean(beanName, mbdToUse, args);


image

這里定義了一個instanceWrapper廓啊,讀者可以將其當做一個有一些功能的對象欢搜,然后進入instanceWrapper = createBeanInstance(beanName, mbd, args);


image

這里主要是獲取class類型,然后判斷是不是FactoryMethod創(chuàng)建對象谴轮,如果不是就繼續(xù)往下:


image

這里其實是一種緩存技術炒瘟,如果我們在前面已經(jīng)確認了這個類的構造器是什么,那么久不會再進行下文的構造器策略第步,直接調(diào)用構造器方法疮装,當然我們這里暫時沒有缘琅。


image

這里是Spring對實例構造器的一個策略,我們都知道一個類創(chuàng)建一個對象無非就兩種情況廓推,調(diào)用有參構造函數(shù)或者無參構造函數(shù)刷袍,那么我們先從無參構造說起,看下圖


image

假如我們的ctors為空 也就是說我們的對象是用的無參構造樊展,那么會執(zhí)行到return instantiateBean(beanName, mbd);呻纹,我們跟蹤此代碼


image

在跟蹤beanInstance =getInstantiationStrategy().instantiate(mbd, beanName, parent);->return BeanUtils.instantiateClass(constructorToUse);->


image

最終可以看到我們的反射代碼newInstance。介紹完無參構造器創(chuàng)建對象后专缠,我們開始探討第二種有參構造創(chuàng)建對象的方式雷酪,回到IDEA,首先我們對User1類新增一個有參構造器藤肢,


image

然后我們開始debug調(diào)試太闺,我們以User1類作為調(diào)試類


image

然后進入determineConstructorsFromBeanPostProcessors方法


image

這里會拿出很多后置處理器,但前面的基本都是默認實現(xiàn)嘁圈,真正干活的是這個類AutowiredAnnotationBeanPostProcessor
然后進入:


image

前面的方法是檢查是否存在LOCKUP方法,我們這里沒有執(zhí)行下一步


image

這里也沒做什么事情蟀淮,只是想從緩存中拿出有沒有確定好的構造器最住,后面會獲取所有構造方法,判斷方法是不是加了@Awtowire注解怠惶,基本都會跳過涨缚,我們直接跳到核心代碼


image

這里其實就是確認構造器,從我們的斷點來看策治,當我們拿到的構造函數(shù)只有一個脓魏,且參數(shù)大于0,從我們目前的情況來看是符合的通惫,那么就會返回我們構造器茂翔。拿到構造器后我們再看


image

我們會拿到我們所創(chuàng)建的構造器,那如果我們換一種方式采用多個構造器


image

我們調(diào)試發(fā)現(xiàn)
image

返回的值為null履腋,其實這也很好理解珊燎,在沒有指定某個構造器primary時,如果我們創(chuàng)建多個構造器遵湖,Spring不知道如何決策悔政,就只執(zhí)行默認構造器


image

,在上文我們對無參構造的方式進行了介紹延旧,現(xiàn)在我們對有參構造進行說明
image

autowireConstructor()->autowireConstructor()谋国;
這里我們思考下如何用有參構造的方式創(chuàng)建對象,首先是不是確定我們的類迁沫,其次是不是需要確定構造器芦瘾,然后我們還需要確定參數(shù)的值是什么捌蚊,那么下面的操作就是確定參數(shù)的值到底是什么,我們回到IDEA:


image

這里沒什么需要多說的


image

這里看有沒有緩存,顯然我們沒有


image

這里的代碼個人認為Spring可能為了防止其他地方復用這段代碼,所以判斷構造器是不是為空


image

這里autowiring肯定不會false囤热,然后進入有參構造十分關鍵的代碼


image

通過方法名我們都能大概猜出來這里就是獲取有參構造傳入的參數(shù)值院溺,前面我們說過,確定構造器還需要構造參數(shù)的值才能成功創(chuàng)建一個對象讯嫂,那么我們看這個代碼能否獲取參數(shù)值


image

很顯然這里并沒有獲取,道理很簡單,因為我們并沒有對User1類指定構造器參數(shù)值迫摔,如果我們繼續(xù)運行下去,Spring會拋出異常泥从,表示無法創(chuàng)建這個類句占,這樣很好說明,我們想要使用有參構造創(chuàng)建對象躯嫉,確定沒有給定參數(shù)值纱烘,這必然會報錯,
image

那么怎樣才能獲取參數(shù)值呢祈餐,這里筆者會采用兩種方式:第一種方式筆者會改變Spring源碼


image

然后我們再次運行
image

擂啥,發(fā)現(xiàn)已經(jīng)有值,然后User1類會創(chuàng)建對象成功帆阳,當然我們?nèi)ジ淖冊创a是不是不太合適啊哺壶,所以采用第二種方式后置處理器,代碼如下:


image

依舊可以實現(xiàn)有參構造的方式蜒谤,

讀到這里山宾,讀者是否有些恍然大悟,領略到了筆者前面提出的Spring的后置處理器的作用和設計的精妙鳍徽,這樣就可以實例化一個對象了资锰,當然這還沒有完,Spring還會對其他特別的有參構造進行解析旬盯,因為設計思想較為復雜台妆,讀者有興趣可以自行深入了解,那么我們的對象實例化就算結(jié)束了胖翰,回到IDEA接剩,
image

這里我們返回了我們的對象。

Bean初始化

image

在創(chuàng)建完對象后萨咳,Spring將會對象進行初始化懊缺,我們跟蹤applyMergedBeanDefinitionPostProcessors這段代碼:


image

可以看到這又是循環(huán)后置處理器,獲取屬于MergedBeanDefinitionPostProcessor,然后我們繼續(xù)跟蹤bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);在查看這個方法到底會執(zhí)行哪些處理器鹃两,


image

我們在對其中之一后置處理器點開


image
image

其實這里的處理器就是對我們Spring的生命周期的處理遗座,但這里并不會執(zhí)行,而是采用元信息的方式將它保存起來俊扳,好了途蒋,我們繼續(xù)往下看


image

前面的代碼不重要,我們直接看到addSingletonFactory(beanName, () ->
getEarlyBeanReference(beanName, mbd, bean));


image

這里會有四個集合馋记,第一個集合是singletonObjects存放單例Bean的号坡,第二個singletonFactories
存放的是ObjectFactory工廠Bean,這個ObjectFactory個人認為和Bean并無太大區(qū)別梯醒,直接間接存取而已宽堆,第三個earlySingletonObjects是提前存儲的對象,最后一個registeredSingletons存放的是BeanName茸习,用來解決排序問題畜隶,好了我們繼續(xù)


image

接下來可以看到Spring的屬性注入的核心代碼populateBean(beanName, mbd,
instanceWrapper);,在對這段代碼分析之前号胚,我們先修改一下我們的工程籽慢,首先將User2改成


image

再將User1改成
image

這條調(diào)整的目的是為了演示循環(huán)依賴,好了猫胁,
然后我們重新debug


image

這里先對User1屬性注入嗡综,我們點進去


image

這里又有對后置處理器的處理,這里后置處理器的作用是如果你實現(xiàn)了InstantiationAwareBeanPostProcessors的postProcessAfterInstantiation方法杜漠,那么Spring會認為你牛逼,你要自己初始化察净,那我就中斷這個Bean的后續(xù)一切操作驾茴,就等于脫離Spring的生命周期管理,其實這也是可以想到的氢卡,不一定所有的對象都是需要Spring幫我們處理锈至,這里Spring的設計考慮的十分周全,我們回到IDEA


image

這里有一個重要的屬性叫AutowireMode译秦,為什么說他重要峡捡,我們會在Spring-Mybatis章節(jié)詳細說明,這里先跳過筑悴,因為我們默認的是NO们拙,然后我們進入這里


image

看方法名能夠大概猜到這里就是屬性注入的關鍵,我們斷點


image

然后我們直接跳到AutowiredAnnotationBeanPostProcessor阁吝,進入這個方法PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);首先獲取我們的InjectionMetadata砚婆,注入需要的元數(shù)據(jù)


image

然后->metadata.inject(bean, beanName, pvs);->element.inject(target, beanName, pvs);

然后我們先看最后一行代碼
image

field.set(bean, value);懂反射的小伙伴們都知道,我們可以通過Field類對一個類的對象屬性賦值突勇,所以Spring默認也采用的這種方式装盯,那么我們現(xiàn)在有了類坷虑,缺少value,而這個value在元數(shù)據(jù)中已經(jīng)顯示User2類埂奈,所以我們需要拿到User2的對象才行迄损,回到IDEA上面的代碼,其中有一行


image

然后我們跟蹤下resolveDependency->doResolveDependency->descriptor.resolveCandidate


image

最終又回到了getBean账磺,也就是說Spring會從工廠里拿芹敌,而這個時候我們的工廠并沒有創(chuàng)建它,所以User2創(chuàng)建對象后依舊會進行進入屬性注入的方法


image

這個時候我們跳到了User2的屬性注入方法绑谣,然后一樣User2里面有User1這個類党窜,所以又會去獲取User1的對象,但是我們的User1并沒有創(chuàng)建完成借宵,屬于創(chuàng)建中的狀態(tài)幌衣,那么Spring是如何解決的呢?我們繼續(xù)跟蹤代碼


image

這里的代碼是我們先創(chuàng)建了User1然后屬性注入user2壤玫,這時工廠沒有User2對象豁护,于是創(chuàng)建User2,User2創(chuàng)建完后開始對內(nèi)部的屬性user1注入欲间,這時會跳到上圖楚里,然后我們再跟蹤進去


image

singletonObject == null 目前是為空的,因為我們的user1還在創(chuàng)建中猎贴,然而這一行代碼我們運行isSingletonCurrentlyInCreation(beanName)


image

返回的是true班缎,還記我們創(chuàng)建對象時會將對象標識為正在創(chuàng)建嗎?這里就起到了效果她渴,那么if會成立达址,這個時候就會從我們的ObjectFactory獲取,在上文提到過ObjectFactory是間接存取趁耗,


image

隨后User2拿到了User1的對象沉唠,然后User1也會拿到User2的對象因此解除循環(huán)依賴,創(chuàng)建對象成功苛败,最后
image

這里會獲取我們前文存儲的生命周期元數(shù)據(jù)满葛,并執(zhí)行


image
image

我們用一張圖回顧下我們的循環(huán)依賴


image

目前對Spring的整體核心源碼基本已經(jīng)告一段落,后面會對AOP罢屈、Spring-Mybatis嘀韧、事物、數(shù)據(jù)來源儡遮、事件等知識結(jié)合面試題的方式進行嵌套描述乳蛾。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子肃叶,更是在濱河造成了極大的恐慌蹂随,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件因惭,死亡現(xiàn)場離奇詭異岳锁,居然都是意外死亡,警方通過查閱死者的電腦和手機蹦魔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門激率,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人勿决,你說我怎么就攤上這事乒躺。” “怎么了低缩?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵嘉冒,是天一觀的道長。 經(jīng)常有香客問我咆繁,道長讳推,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任玩般,我火速辦了婚禮银觅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘坏为。我一直安慰自己究驴,他們只是感情好,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布匀伏。 她就那樣靜靜地躺著纳胧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪帘撰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天万皿,我揣著相機與錄音摧找,去河邊找鬼。 笑死牢硅,一個胖子當著我的面吹牛蹬耘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播减余,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼综苔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起如筛,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤堡牡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后杨刨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晤柄,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年妖胀,在試婚紗的時候發(fā)現(xiàn)自己被綠了芥颈。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡赚抡,死狀恐怖爬坑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情涂臣,我是刑警寧澤盾计,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站肉康,受9級特大地震影響闯估,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吼和,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一涨薪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧炫乓,春花似錦刚夺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至箩做,卻和暖如春莽红,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背邦邦。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工安吁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人燃辖。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓鬼店,卻偏偏與公主長得像,于是被迫代替她去往敵國和親黔龟。 傳聞我的和親對象是個殘疾皇子妇智,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345