Spring讀書(shū)筆記——bean創(chuàng)建(下)

有關(guān)Spring加載bean系列债沮,今天這是最后一篇了,主要接上篇對(duì)于從Spring容器中獲取Bean的一些細(xì)節(jié)實(shí)現(xiàn)的補(bǔ)充晓铆。

從緩存中加載單例

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 != NULL_OBJECT ? singletonObject : null);
}

在看這段代碼之前蟆融,我們先了解下Spring對(duì)于單例bean出現(xiàn)循環(huán)依賴(lài)的解決方法铅檩。如果出現(xiàn)上面我們介紹的A->B->C->A的情況,那是不是說(shuō)Spring就無(wú)能為力了粱玲,顯然Spring沒(méi)有那么弱躬柬。那么Spring是怎么做的?
鑒于單例bean的循環(huán)依賴(lài)問(wèn)題密幔,Spring創(chuàng)建bean的原則是不等bean創(chuàng)建完成就會(huì)將bean的ObjectFactory提前曝光加入到緩存中楔脯,一旦有某個(gè)bean創(chuàng)建時(shí)需要依賴(lài)這個(gè)bean了,那么就可以直接使用ObjectFactory胯甩。
簡(jiǎn)單說(shuō)昧廷,創(chuàng)建bean的時(shí)候,就是打包快遞發(fā)貨偎箫,主管為了知道你今天要派發(fā)多少個(gè)包裹木柬,為了節(jié)省大家時(shí)間以及以免統(tǒng)計(jì)漏掉的情況。你可以先拿出一個(gè)包裹箱子淹办,上面寫(xiě)上要寄收件人眉枕、收貨地址、聯(lián)系方式等等怜森,但是這時(shí)候還沒(méi)有往里面打包真正的快遞速挑。
這里曝光的bean就相當(dāng)于這個(gè)快遞箱子。

好了副硅,知道了這個(gè)原則之后姥宝,我們就好理解代碼了。
首先從singletonObjects中獲取實(shí)例恐疲,取不到則從earlySingletonObjects中獲取腊满,仍然取不到,我們還可以到singletonFactories中獲取相應(yīng)的ObjectFactory培己,在調(diào)用這個(gè)ObjectFactory的getObject方法來(lái)創(chuàng)建bean碳蛋。
然后將其加入到earlySingletonObjects中,在將其從singletonFactories中刪除省咨。

想必肃弟,你已經(jīng)被這些用來(lái)存儲(chǔ)和刪除的集合搞瘋了,沒(méi)關(guān)系茸炒,我們來(lái)理一下:

  • singletonObjects
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);

用于保存BeanName和創(chuàng)建bean實(shí)例之間的關(guān)系愕乎。

  • singletonFactories:
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory> singletonFactories = new HashMap<String, ObjectFactory>(16);

用于保存BeanName和創(chuàng)建bean的工廠之間的關(guān)系

  • earlySingletonObjects:
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

用于保存BeanName和創(chuàng)建bean的工廠之間的關(guān)系阵苇,與singletonObjects的區(qū)別是當(dāng)一個(gè)bean被放入這個(gè)集合后,可以用于其他bean做循環(huán)依賴(lài)檢查

bean實(shí)例化

我們從緩存中拿到bean之后感论,就需要根據(jù)bean的不同類(lèi)型做不同的處理绅项,返回相應(yīng)的bean,實(shí)現(xiàn)這個(gè)功能的就是getObjectForBeanInstance方法

protected Object getObjectForBeanInstance(
 Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {

 // Don't let calling code try to dereference the factory if the bean isn't a factory.
 if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
 throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
 }

 // Now we have the bean instance, which may be a normal bean or a FactoryBean.
 // If it's a FactoryBean, we use it to create a bean instance, unless the
 // caller actually wants a reference to the factory.
 if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
 return beanInstance;
 }

 Object object = null;
 if (mbd == null) {
 object = getCachedObjectForFactoryBean(beanName);
 }
 if (object == null) {
 // Return bean instance from factory.
 FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
 // Caches object obtained from FactoryBean if it is a singleton.
 if (mbd == null && containsBeanDefinition(beanName)) {
 mbd = getMergedLocalBeanDefinition(beanName);
 }
 boolean synthetic = (mbd != null && mbd.isSynthetic());
 object = getObjectFromFactoryBean(factory, beanName, !synthetic);
 }
 return object;
 }
  • 首先檢測(cè)指定的beanName是否是工廠bean相關(guān)比肄,如果既不是工廠bean又是以"&"開(kāi)頭快耿,則校驗(yàn)失敗,拋出異常
  • 如果這個(gè)bean不是工廠Bean(FactoryBean)芳绩,那么就直接返回bean實(shí)例
  • 剩下代碼就是處理FactoryBean掀亥,我們順著這樣的順序依次來(lái)到getObjectForBeanInstance->getObjectFromFactoryBean->doGetObjectFromFactoryBean
private Object doGetObjectFromFactoryBean(
 final FactoryBean factory, final String beanName, final boolean shouldPostProcess)
 throws BeanCreationException {

 Object object;
 try {
 if (System.getSecurityManager() != null) {
 AccessControlContext acc = getAccessControlContext();
 try {
 object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
 public Object run() throws Exception {
 return factory.getObject();
 }
 }, acc);
 }
 catch (PrivilegedActionException pae) {
 throw pae.getException();
 }
 }
 else {
 object = factory.getObject();
 }
 }
 catch (FactoryBeanNotInitializedException ex) {
 throw new BeanCurrentlyInCreationException(beanName, ex.toString());
 }
 catch (Throwable ex) {
 throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
 }

 // Do not accept a null value for a FactoryBean that's not fully
 // initialized yet: Many FactoryBeans just return null then.
 if (object == null && isSingletonCurrentlyInCreation(beanName)) {
 throw new BeanCurrentlyInCreationException(
 beanName, "FactoryBean which is currently in creation returned null from getObject");
 }

 if (object != null && shouldPostProcess) {
 try {
 object = postProcessObjectFromFactoryBean(object, beanName);
 }
 catch (Throwable ex) {
 throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object failed", ex);
 }
 }

 return object;
 }

這么長(zhǎng)的代碼,如果嫌累妥色,就只看factory.getObject()這行就好搪花,這詮釋了FactoryBean的加載時(shí)通過(guò)factory.getObject的方式獲取到對(duì)應(yīng)的bean實(shí)例的。

如何創(chuàng)建單例bean

在上篇的doGetBean方法中嘹害,如果從緩存中加載不到撮竿,那么我們就需要老老實(shí)實(shí)的從頭開(kāi)始加載bean了,對(duì)于單例bean的加載就都在這里實(shí)現(xiàn)了

// Create bean instance.
 if (mbd.isSingleton()) {
 sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
 public Object getObject() throws BeansException {
 try {
 return createBean(beanName, mbd, args);
 }
 catch (BeansException ex) {
 // Explicitly remove instance from singleton cache: It might have been put there
 // eagerly by the creation process, to allow for circular reference resolution.
 // Also remove any beans that received a temporary reference to the bean.
 destroySingleton(beanName);
 throw ex;
 }
 }
 });
 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
 }

創(chuàng)建bean

我們從AbstractBeanFactory的createBean方法來(lái)到了AbstractAutowiredCapableBeanFactory的createbean方法笔呀,而真正的創(chuàng)建bean其實(shí)在doCreateBean方法中

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {// Instantiate the bean.   BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);// Allow post-processors to modify the merged bean definition.   synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);mbd.postProcessed = true;}}// Eagerly cache singletons to be able to resolve circular references   // even when triggered by lifecycle interfaces like BeanFactoryAware.   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isDebugEnabled()) {logger.debug("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}addSingletonFactory(beanName, new ObjectFactory() {public Object getObject() throws BeansException {return getEarlyBeanReference(beanName, mbd, bean);}});}// Initialize the bean instance.   Object exposedObject = bean;try {populateBean(beanName, mbd, instanceWrapper);if (exposedObject != null) {exposedObject = initializeBean(beanName, exposedObject, mbd);}}catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);}}if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +"] in its raw version as part of a circular reference, but has eventually been " +"wrapped. This means that said other beans do not use the final version of the " +"bean. This is often the result of over-eager type matching - consider using " +"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");}}}}// Register bean as disposable.   try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject;}
  • 如果是單例Bean幢踏,那么首先是從factoryBeanInstanceCache中清除該beanName對(duì)應(yīng)的記錄
  • 實(shí)例化bean,將BeanDefinition轉(zhuǎn)換為BeanWrapper對(duì)象
  • bean合并后的處理
  • 解決循環(huán)依賴(lài)問(wèn)題
  • 屬性填充许师,將所有屬性填充到bean的實(shí)例中
    這個(gè)方法房蝉,本身不算長(zhǎng),但是層層深入就會(huì)發(fā)現(xiàn)其下面包羅了創(chuàng)建bean的諸多繁雜的細(xì)節(jié)(這塊自己看的也是云里霧里微渠,就不往下延伸擴(kuò)展了)搭幻。

雖然對(duì)于Spring加載bean,我只寫(xiě)了四篇逞盆,但是其內(nèi)部實(shí)現(xiàn)遠(yuǎn)比我表述的要復(fù)雜的多粗卜。
看源碼確實(shí)很煎熬,對(duì)于目前看不懂的地方要么多看幾遍纳击,要么先跳過(guò)。閱讀代碼的過(guò)程中要懂得取舍攻臀,對(duì)于非重點(diǎn)部分比如日志或者異常處理可以先忽略焕数,沿著一條主線往下看,最主要是先弄懂代碼的只要意圖刨啸。
Spring的bean加載代碼量雖然巨大堡赔,但是思路還是比較清晰的,我們知道Spring如何加載xml然后解析xml设联,再到如何把xml的元素轉(zhuǎn)為自己的BeanDefinition善已,最后又是如何取出對(duì)應(yīng)的beanName然后返回一個(gè)bean實(shí)例供容器使用的灼捂。

網(wǎng)上有一位大神用一張圖就把整個(gè)過(guò)程畫(huà)出來(lái)了

image.png

注:圖片來(lái)源http://blog.csdn.net/zghwaicsdn/article/details/50910384

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市换团,隨后出現(xiàn)的幾起案子悉稠,更是在濱河造成了極大的恐慌,老刑警劉巖艘包,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件的猛,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡想虎,警方通過(guò)查閱死者的電腦和手機(jī)卦尊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)舌厨,“玉大人岂却,你說(shuō)我怎么就攤上這事∪雇郑” “怎么了躏哩?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)骇陈。 經(jīng)常有香客問(wèn)我震庭,道長(zhǎng),這世上最難降的妖魔是什么你雌? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任器联,我火速辦了婚禮,結(jié)果婚禮上婿崭,老公的妹妹穿的比我還像新娘拨拓。我一直安慰自己,他們只是感情好氓栈,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布渣磷。 她就那樣靜靜地躺著,像睡著了一般授瘦。 火紅的嫁衣襯著肌膚如雪醋界。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,268評(píng)論 1 309
  • 那天提完,我揣著相機(jī)與錄音形纺,去河邊找鬼。 笑死徒欣,一個(gè)胖子當(dāng)著我的面吹牛逐样,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼脂新,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼挪捕!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起争便,我...
    開(kāi)封第一講書(shū)人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤级零,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后始花,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體妄讯,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年酷宵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了亥贸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浇垦,死狀恐怖炕置,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情男韧,我是刑警寧澤朴摊,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站此虑,受9級(jí)特大地震影響甚纲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜朦前,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一介杆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧韭寸,春花似錦春哨、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至晶渠,卻和暖如春凰荚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背褒脯。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工浇揩, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人憨颠。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親爽彤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子养盗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

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

  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,852評(píng)論 6 342
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)适篙,斷路器往核,智...
    卡卡羅2017閱讀 134,701評(píng)論 18 139
  • 文章作者:Tyan博客:noahsnail.com 3.5 Bean scopes When you create...
    SnailTyan閱讀 1,893評(píng)論 0 1
  • 東野圭吾的書(shū)總是能讓人達(dá)到廢寢忘食的地步,迫不及待想要知道結(jié)局的欲望驅(qū)使著你嚷节,意想不到的故事情節(jié)冥冥中又在告訴你他...
    黃美美閱讀 562評(píng)論 1 2
  • 一個(gè)女人最迷人的時(shí)候就是當(dāng)她不愛(ài)你的時(shí)候聂儒,如果她能少喜歡你一點(diǎn),你會(huì)發(fā)現(xiàn)對(duì)方是個(gè)特別可愛(ài)的人硫痰。有時(shí)候衩婚,愛(ài)會(huì)讓人變得...
    深夜下的莫吉托閱讀 296評(píng)論 0 0