前面兩篇,我們著重講解了一下《BeanDefinition的加載》和《bean的實(shí)例化》。
河口ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)公司的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書合作)期待與您的合作!這一篇我們來講解一下bean的初始化。
我們這里的案例依舊是以SpringBoot3.0、JDK17為前提,案例代碼如下:
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
@Autowired
private A a;
}
首先,先明確一下這個三級緩存:
一級緩存 singletonObjects 中存放完全初始化好的 bean 的實(shí)例。
二級緩存 earlySingletonObjects中存放早期對象(未完全初始化完成的 bean 實(shí)例)。
三級緩存 singletonFactories 中存放 bean 工廠對象。
bean 創(chuàng)建起來比較復(fù)雜,所以將bean緩存起來,方便之后使用。
上一篇,我們實(shí)例化了bean之后,將bean放入了第三級緩存,看一下這個addSingletonFactory方法,如果一級緩存中沒有對應(yīng)的bean,那么會將未初始化的bean放入三級緩存,會將bean提前暴露出來。
接下來,會通過populateBean()
方法對bean的屬性進(jìn)行填充。
步入populateBean()
方法,首先會獲取BeanPostProcessors,主要是在bean初始化之前做一些事情,如下,我們總共獲取到4個BeanPostProcessors ,最后一個AutowiredAnnotationBeanPostProcessor是本節(jié)較為關(guān)鍵的BeanPostProcessor,它主要是用來處理我們@Autowired注解。
我們進(jìn)入到AutowiredAnnotationBeanPostProcessor#postProcessProperties方法中,首先有個findAutowiringMetadata方法來處理@Autowired
我們步入findAutowiringMetadata方法中,發(fā)現(xiàn)會從injectionMetadataCache的map集合中獲取。
但是我們沒有往這個緩存中put對象啊,所以,這個metadata是null,而后,我們會簡單的判斷一下:
public static boolean needsRefresh(@Nullable InjectionMetadata metadata, Class>clazz) {
return (metadata == null || metadata.needsRefresh(clazz));
}
如果為null,我們會調(diào)用buildAutowiringMetadata方法,在這里我們會看到很熟悉的設(shè)計(jì),就像單例模式一樣的雙重鎖/雙重校驗(yàn)鎖(DLC,即double-checked locking)
而后會進(jìn)入buildAutowiringMetadata方法去創(chuàng)建相關(guān)信息。
這里主要有兩點(diǎn),一個是針對屬性上的注解,一個是針對方法上的注解,大部分的時候會放在屬性上,也有時候會放在方法上,比如set方法上。
我們來一起看一下這個處理屬性的doWithLocalFields方法,首先通過getDeclaredFields方法獲取類的屬性集合,然后進(jìn)行遍歷調(diào)用函數(shù)去處理。
我們來看一下是如何獲取類的屬性數(shù)組的,還用想嘛,當(dāng)然是反射了,不信你看。第一次從屬性緩存中獲取,肯定獲取不到啊,然后反射獲取,在加入緩存中。
然后包裝成AutowiredFieldElement對象,add到currElements列表中,最后在把各個bean的所有元素add到elements列表中。
最后會通過InjectionMetadata#forElements方法將所有的元素封裝成InjectionMetadata對象返回。
最后把注入元素的信息放進(jìn)injectionMetadataCache的map集合中,以便下次就可以從緩存中獲取。
到這里,我們就解析完了帶有@Autowired注解的屬性,接下來進(jìn)入我們的核心方法inject。
在這里首先會判斷bean中是否有屬性,如果有,循環(huán)遍歷屬性,調(diào)用inject方法。
屬性準(zhǔn)備就緒,著重看一下resolveFieldValue方法
步入上圖的789行
步入doResolveDependency方法,最后會對這個屬性B進(jìn)行解析
步入resolveCandidate方法,這不是beanFactory么,真巧,來了老弟。
這不是想去緩存里找么,嘿嘿
此時b還沒有實(shí)例化,緩存中肯定找不到啊。
找不到就去創(chuàng)建吧
嗖,創(chuàng)建完了。
創(chuàng)建完又要填充屬性了,B的屬性是A,來吧,在走一圈。
最后還是要去beanFactory中去找,因?yàn)樵赼填充屬性的時候,a就已經(jīng)實(shí)例化過了,也已經(jīng)put進(jìn)三級緩存暴漏出來了,我們來看看是如何處理的。
老樣子,看看248行代碼。
哎,臥槽,怎么直接跳到下面來了?bean工廠里沒有?那不對啊,按理來說應(yīng)該可以從三級緩存中找到啊。服了,看看吧。
經(jīng)過再一次的debug發(fā)現(xiàn),這個allowCircularReferences屬性默認(rèn)是false,在SpringBoot2.6.x之前都是默認(rèn)為true。
如果需要開啟循環(huán)依賴需要在配置文件配置:
spring:
main:
allow-circular-references: true
坑,SpringBoot2.6.x默認(rèn)禁用循環(huán)依賴。
開啟之后,再來一遍吧。
獲取a —》先去緩存中取,結(jié)果都沒有—》創(chuàng)建,a實(shí)例化—》添加進(jìn)三級緩存beanFactory—》填充屬性b—》獲取b—》先去緩存中取,緩存中也沒有—》創(chuàng)建,b實(shí)例化—》添加進(jìn)三級緩存beanFactory—》填充屬性a—》先去緩存中找,這下肯定有了,不信我們看看
你看,有了吧,啥都有了。也能從三級緩存中取到,并且把這個bean放進(jìn)二級緩存earlySingletonObjects中。
然后就會進(jìn)入如下這個if判斷中,getObjectForBeanInstance方法中會做一些判斷如果是當(dāng)前實(shí)例,則會直接返回該實(shí)例。
最后得到a,退出解析方法
然后將這個屬性值放進(jìn)緩存中
退出該方法
然后把得到的A賦值給B的屬性
就此,B的屬性A賦值結(jié)束。此時可以看到B是有值的,屬性A也是有值的,可是A的屬性B還沒有賦值呢。
此時我們已經(jīng)創(chuàng)建完b,也就是說已經(jīng)調(diào)完了上述的那個lambda函數(shù),緊接著會調(diào)用addSingleton方法,將bean放進(jìn)一級緩存。
將b放進(jìn)一級緩存同樣也標(biāo)志著b初始化結(jié)束,緊接著,a會將b設(shè)置為自己的屬性。最后也會把a(bǔ)放進(jìn)一級緩存,就此over~
當(dāng)然,這里可能還存在很多問題,我給的這個demo,并不能完全展示出二級緩存的作用,因?yàn)閍只有一個屬性b,那么填充屬性之后,a就已經(jīng)存在一級緩存中。
可以這樣,A同時依賴于B和C,B和C都依賴于A。這樣就可以看出二級緩存的作用了。
其實(shí)對于解決循環(huán)依賴來說,兩個緩存就完全足夠,一個存放未初始化的bean,一個存放已經(jīng)初始化的bean,那么要三級緩存干什么?
具體原因我們可以放在下一篇aop的源碼解析。
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧