淺談Spring中的getBean

? ? getBean對Spring來說说榆,是一個非常重要的方法喉誊,用于實例化對象。比如在獲取BeanProcessor的時候羔挡,就用到了beanFacroty.getBean來獲取BeanProcessor對象洁奈。從doGetBean出發(fā)。

getSingleton

? ? 這邊涉及到了幾個類里面的參數(shù)先提及一下:

? ? singletonObjects:用Map類型的該參數(shù)來存放beanName和bean實例之間的關(guān)系绞灼,bean的生命周期已結(jié)束(一級緩存)利术;

? ? singletonCurrentlyInCreation:正在實例化的單例;

? ? earlySingletonObjects:提前暴露的單例實例低矮,bean的生命周期還未結(jié)束(二級緩存)印叁;

? ? singletonFactories:用Map類型的該參數(shù)來存放beanName和beanFactory之間的關(guān)系(三級緩存);

? ? 先從一級緩存中根據(jù)beanName拿值军掂,如果拿到了就返回轮蜕,沒有拿到的話,先去判斷下beanName代表的實例是否正在創(chuàng)建蝗锥,如果是在創(chuàng)建中跃洛,那就去二級緩存中拿值,如果沒拿到终议,就去三級緩存中根據(jù)beanName獲取到BeanFactory對象税课,通過BeanFactory的getObject拿到實例,然后重新將值賦給二級緩存痊剖,再從三級緩存中移除beanName和其BeanFactory韩玩,如圖1。

圖1

????為什么需要這么麻煩設(shè)計三次緩存來獲取實例陆馁,為什么不像自定義Spring一樣只要用一個singletonObjects就能解決獲取實例的問題找颓?是因為我們在使用Spring的過程中,很多時候涉及到了循環(huán)依賴叮贩,本次只講getBean击狮,循環(huán)依賴下一次再講佛析。

? ? 如果getSingleton獲取到了實例,還回去判斷當(dāng)前返回的是不是和工廠相關(guān)的(beanName有“&”前綴)彪蓬,如果是寸莫,但是并不是FactoryBean類型的就會報錯。所以現(xiàn)在的獲取到的實例要么是個正常的bean档冬,要么是工廠bean膘茎。如果是正常的bean就直接返回,如圖2:

圖2

????如果是工廠bean酷誓,先去緩存中嘗試獲取bean披坏,如果成功了直接返回,如果不成功盐数,調(diào)用getObjectFromFactoryBean棒拂,如圖3。

圖3

????最終還是會通過調(diào)用doGetObjectFromFactoryBean方法里工廠的getObject操作獲取bean實例玫氢,如圖4:

圖4

? ? 不過一般第一次getSingleton是獲取不到實例的帚屉,走的是else這條路線:

? ? 接下來就是看是否有父類工廠、dependsOn依賴于其他實例的創(chuàng)建漾峡,有的話要它們先創(chuàng)建涮阔,這邊就不看了。往下看代碼發(fā)現(xiàn)又出現(xiàn)了一個getSingleton方法灰殴,里面有四個比較重要的方法:

1敬特、beforeSingletonCreation

圖5

? ? 該方法中有兩個類參數(shù):inCreationCheckExeclusions(排除正在創(chuàng)建檢查的beanName的集合,這個集合目前遇到的比較少牺陶,知道有這個屬性即可)伟阔,以及singletonCurrentlyIncreation(這個集合里的都是正在進(jìn)行實例化的bean,就是實例化還為完成的beanName掰伸,防止bean對象在進(jìn)行循環(huán)依賴引用的時候反復(fù)創(chuàng)建)皱炉。往singletonCurrentlyIncreation中添加正在實例化的beanName。

2狮鸭、singletonFactory.getObject()

? ? 通過工廠創(chuàng)建實例合搅。

3、afterSingletonCreation

圖6

? ? 實例化完成后從singletonCurrentlyIncreation中刪除對應(yīng)的beanName歧蕉。

4灾部、addSingleton

圖7

? ? 二、三級緩存刪除對應(yīng)的beanName惯退,一級緩存添加beanName和bean實例的對應(yīng)關(guān)系赌髓,再往registeredSingletons添加beanName代表已經(jīng)完成的bean實例化。

-----------------------------------------------------------------------------------------------------------------------------------

createBean

? ? 關(guān)注doCreateBean,如果是單例锁蠕,嘗試用beanName直接從之前的保存在factoryBeanInstanceCache中獲取BeanWrapper夷野,如果不行則調(diào)用createBeanInstnce來創(chuàng)建BeanWrapper(BeanWrapper是對bean的包裝,是bean到BeanDefinition的中間產(chǎn)物荣倾,用來為bean做屬性填充)悯搔。

createBeanInstance

? ? 用于生成BeanWrapper的方法,有三種實現(xiàn):

圖8

? ? 1舌仍、如果當(dāng)前BeanDefinition有instanceSupplier屬性妒貌,就以此來生成BeanWrapper,在使用Spring的過程中抡笼,可以通過如圖9所示來使用:

圖9

? ? 2苏揣、如果有factory-method屬性黄鳍,調(diào)用instantiateFactoryMethod生成BeanWrapper推姻。

? ? 3、對于沒有上面兩個屬性的框沟,也可以通過構(gòu)造函數(shù)進(jìn)行實例化藏古,分為有參構(gòu)造函數(shù)和無參構(gòu)造函數(shù)。有參構(gòu)造函數(shù)先通過determineConstructorsFromBeanPostProcessors獲取到含有@Autowired注解的構(gòu)造函數(shù)忍燥,然后調(diào)用autowireConstructor生成BeanWrapper拧晕。無參構(gòu)造函數(shù)直接調(diào)用instantiateBean生成BeanWrapper。

applyMergedBeanDefinitionPostProcessors

? ? 用來對類中的注解進(jìn)行裝配梅垄,是通過BeanPostProcessor的postProcessMergedBeanDefinition進(jìn)行裝配厂捞,列舉下面兩個比較重要的實現(xiàn)類:

CommonAnnotationBeanPostProcessor

? ? 這個類中的解析分為兩部分:一個是postProcessMergedBeanDefinition來掃描@PostConstruct和@PreDestory注解;一個是findResourceMetadata來掃描@Resource注解队丝。

postProcessMergedBeanDefinition:先根據(jù)beanType去緩存lifecycleMetadataCache中尋找LifecycleMetadata靡馁,如果未找到則通過buildLifeCycleMetadata找到里面的initMethod和的storyMethods方法,以及類本身机久,會被包裝成LifeCycleMetadata對象臭墨,如圖10,并放入緩存lifeCycleMetadataCache膘盖,如圖11:

圖10
圖11

然后通過checkConfigMembers對當(dāng)前的BeanDefinition進(jìn)行掃描胧弛,把其中的initMethod和的storyMethod拿出來,放入類參數(shù)checkedInitMethods和checkedDestoryMethods中侠畔,如圖12:

圖12

findResourceMetadata和checkedConfigMembers:和上面掃描初始化和銷毀方法一樣结缚,也有一個緩存參數(shù)injectionMetadataCache,如果找不到也是要通過buildResourceMetadata構(gòu)建软棺,將里面有@Resource注解的元素放入injectedElements中掺冠,如圖13,構(gòu)建完后放入緩存,返回InjectionMetadata德崭,如圖14:

圖13
圖14

????通過checkConfigMembers對當(dāng)前的BeanDefinition進(jìn)行掃描斥黑,把含有@Resource注解的屬性或者方法拿出來,遍歷的就是上一步存到類中的元素集合眉厨,然后放到類參數(shù)checkedElements中锌奴,如圖15:

圖15

AutowiredAnnotationBeanPostProcessor

? ? 功能就是用來掃描方法或者屬性上的@Autowired注解,通過buildAutowiringMetadata方法構(gòu)建元數(shù)據(jù)憾股,將其放入injectionMetadataCache緩存中鹿蜀。然后對BeanDefinition進(jìn)行checkConfigMembers掃描,把含有@Autowired注解的屬性或者方法拿出來服球,放到類參數(shù)checkedElements中茴恰,和上述方式差不多,如圖16:

圖16

earlySingletonExposure

? ? 用來判斷是否支持進(jìn)行提前暴露操作斩熊,會根據(jù)當(dāng)前BeanDefinition是否單例往枣、是否允許循環(huán)依賴(一般允許),以及在singletonCurrentlyInCreation中是否存在當(dāng)前beanName粉渠,如圖17:

圖17

????如果都符合條件分冈,調(diào)用getEarlyBeanReference獲取到提前暴露的實例,并且在三級緩存中添加beanName和單例工廠霸株,移除二級緩存中提前暴露的實例雕沉,如圖18:

圖18

populateBean

? ? 這是IOC/DI依賴注入的核心方法,直接拉下來看關(guān)于BeanPostProcessor的應(yīng)用去件,如圖19:

圖19

????關(guān)注postProcessProperties方法坡椒,該方法是之前通過postProcessMergedBeanDefinition掃描注解并收集后對其元數(shù)據(jù)執(zhí)行inject方法。以@Autowired為例尤溜,如圖20:

圖20

????首先根據(jù)beanName尋找到InjectionMetadata倔叼,通過element.inject方法對已經(jīng)收集到的注解進(jìn)行反射調(diào)用,比如注解在字段上靴跛,則會使用field.set(bean, value)缀雳;在方法上,則會使用method.invoke(bean, arguements)梢睛。

圖21

? ? 對于在xml中配置的屬性肥印,如<property>標(biāo)簽內(nèi)部屬性的依賴注入,則是通過下面applyPropertyValues方法實現(xiàn)的绝葡,如圖22深碱,因為現(xiàn)在基于XML配置的很少使用了,就不必去看是怎么添加屬性的藏畅。

圖22

initializeBean

? ? 這個方法是在實例化和IOC/DI注入后的操作敷硅,首先是對某些Aware接口的調(diào)用功咒,如圖23:

圖23

????然后調(diào)用了applyBeanPostProcessorsBeforeInitialization對類中某些特殊方法的調(diào)用,也是會先通過beanName拿到元數(shù)據(jù)绞蹦,然后通過invoke方法進(jìn)行反射調(diào)用力奋,如圖24:

圖24

? ? 再是通過invokeInitMethods方法,對實現(xiàn)了InitializingBean的類中的afterProperties方法的調(diào)用:

? ? 最后調(diào)用applyBeanPostProcessorsAfterInitialization方法幽七,和before一樣景殷,進(jìn)行反射調(diào)用。

registerDisposableBeanIfNecessary

????在bean創(chuàng)建完成后都會調(diào)用這個方法澡屡,因為用到的都是單例模式的猿挚,所以直接看如下代碼:

圖25

? ? DisposableBeanAdapter對象是來負(fù)責(zé)bean銷毀的類,在new的時候會去掃描BeanDefinition中的destory-method屬性驶鹉,以及其余屬性來構(gòu)建對象绩蜻,然后以beanName為key,生成的DisposableBeanAdapter對象為value室埋,放進(jìn)disposableBeans中办绝。

圖26

----------------------------------------------------------------------------------------------------------------------------------

getObjectForBeanInstance

? ? 里面有個BeanFactoryUtils.isFactoryDereference方法,判斷傳進(jìn)去的name是否攜帶了“&”作為name開頭词顾,只有工廠beanName才會有這種結(jié)構(gòu)八秃,但如果該實例不是FactoryBean碱妆,還是會拋出異常肉盹。如果沒有攜帶“&”作為name的開頭,并且不是FactoryBean疹尾,表明這是個普通實例上忍,直接返回;如果是工廠實例纳本,則需要調(diào)用getObject從工廠中生產(chǎn)實例后返回窍蓝,上面已經(jīng)有類似的流程了,就不截圖了繁成。

-----------------------------------------------------------------------------------------------------------------------------------

Prototype作用域的BeanDefinition

? ? 邏輯和單例的差不多吓笙,但是不像單例這么復(fù)雜,需要考慮重復(fù)創(chuàng)建的問題巾腕;多例作用域只要執(zhí)行了createBean面睛,就是一個新的實例,如圖27:

圖27

-----------------------------------------------------------------------------------------------------------------------------------

埋點

? ??在看源碼的過程中我們經(jīng)匙鸢幔看到如圖28所示的代碼叁鉴,可以看出BeanPostProcessor接口類型是對具有某種特定功能的埋點,根據(jù)對當(dāng)前的BeanPostProcessor進(jìn)行instanceof判斷佛寿,用來過濾掉功能不相關(guān)的BeanPostProcessor幌墓,然后執(zhí)行特定的方法。

圖28

下面來看一下有哪些地方用到了這類埋點:

1、MergedBeanDefinitionPostProcessor:圖28調(diào)用的方法是postProcessorMergedBeanDefinition常侣,收集@Resource蜡饵、@PostConstruct、@PreDestory胳施、@Autowired验残、@Value注解的方法和屬性。

2巾乳、SmartInstantiationAwareBeanPostProcessor:調(diào)用的方法是determineCandidateConstructors您没,用來獲取@Autowired注解的方法和屬性;而在循環(huán)依賴中解決bean提前暴露的實例胆绊,則是通過getEarlyBeanReference氨鹏,如圖29:

圖29

3、InstantiationAwareBeanPostProcessor:調(diào)用的方法是postProcessorAfterInstantiation压状,其中的continueWithPropertyPopulation為true代表可以進(jìn)行依賴注入仆抵;postProcessProperties則是IOC/DI依賴注入的inject方法的埋點,如圖30:

圖30
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末种冬,一起剝皮案震驚了整個濱河市镣丑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌娱两,老刑警劉巖莺匠,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異十兢,居然都是意外死亡趣竣,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門旱物,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遥缕,“玉大人,你說我怎么就攤上這事宵呛〉ハ唬” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵宝穗,是天一觀的道長户秤。 經(jīng)常有香客問我,道長讽营,這世上最難降的妖魔是什么虎忌? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮橱鹏,結(jié)果婚禮上膜蠢,老公的妹妹穿的比我還像新娘堪藐。我一直安慰自己,他們只是感情好挑围,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布礁竞。 她就那樣靜靜地躺著,像睡著了一般杉辙。 火紅的嫁衣襯著肌膚如雪模捂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天蜘矢,我揣著相機與錄音狂男,去河邊找鬼。 笑死品腹,一個胖子當(dāng)著我的面吹牛岖食,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播舞吭,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼泡垃,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了羡鸥?” 一聲冷哼從身側(cè)響起蔑穴,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎惧浴,沒想到半個月后存和,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡赶舆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年哑姚,在試婚紗的時候發(fā)現(xiàn)自己被綠了祭饭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芜茵。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡肠槽,死狀恐怖泽西,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情癣亚,我是刑警寧澤寺鸥,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布猪钮,位于F島的核電站,受9級特大地震影響胆建,放射性物質(zhì)發(fā)生泄漏烤低。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一笆载、第九天 我趴在偏房一處隱蔽的房頂上張望扑馁。 院中可真熱鬧涯呻,春花似錦、人聲如沸腻要。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽雄家。三九已至效诅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間趟济,已是汗流浹背乱投。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留顷编,地道東北人篡腌。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像勾效,于是被迫代替她去往敵國和親嘹悼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354

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