淺談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(這個集合里的都是正在進行實例化的bean,就是實例化還為完成的beanName,防止bean對象在進行循環(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ù)進行實例化,分為有參構(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

? ? 用來對類中的注解進行裝配,是通過BeanPostProcessor的postProcessMergedBeanDefinition進行裝配,列舉下面兩個比較重要的實現(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進行掃描,把其中的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進行掃描,把含有@Resource注解的屬性或者方法拿出來,遍歷的就是上一步存到類中的元素集合,然后放到類參數(shù)checkedElements中,如圖15:

圖15

AutowiredAnnotationBeanPostProcessor

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

圖16

earlySingletonExposure

? ? 用來判斷是否支持進行提前暴露操作,會根據(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)收集到的注解進行反射調(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方法進行反射調(diào)用,如圖24:

圖24

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

? ? 最后調(diào)用applyBeanPostProcessorsAfterInitialization方法,和before一樣,進行反射調(diào)用。

registerDisposableBeanIfNecessary

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

圖25

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

圖26

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

getObjectForBeanInstance

? ? 里面有個BeanFactoryUtils.isFactoryDereference方法,判斷傳進去的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進行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代表可以進行依賴注入;postProcessProperties則是IOC/DI依賴注入的inject方法的埋點,如圖30:

圖30
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容