前言
我是子路,一個(gè)把Java當(dāng)飯吃的人。
筆者之前在華南谷歌搬磚,在系統(tǒng)架構(gòu)設(shè)計(jì)、分布式、微服務(wù)、高并發(fā)、高可用等技術(shù)架構(gòu)具有豐富的實(shí)戰(zhàn)經(jīng)驗(yàn)。對市面上主流的開源框架源碼——spring、nacos,springboot、JDK并發(fā)工具等等都有深入的研究。
Spring是Java語言里面一個(gè)非常重要的框架,可以說任何一個(gè)學(xué)Java的人都必須要接觸到Spring。
這里筆者先給大家好好從源碼的角度來講講Spring。
上文:從源碼的角度來解讀spring的循環(huán)引用(一)——生命周期
↑上文講到了Spring Bean的一個(gè)生命周期,以及用了一些源碼和步驟來佐證。
這篇文章,筆者就來對上文出現(xiàn)的代碼逐行來解釋。
正文
1、deGetBean-1
Object sharedInstance = getSingleton(beanName);

首先這行代碼上有一句spring作者寫的注釋
Eagerly check singleton cache for manually registered singletons.
大概的意思就是檢查一下單例池當(dāng)中有沒有手動注冊的單例對象,說白了spring在創(chuàng)建一個(gè)bean之前先檢查一下beanName是否被手動注冊過到單例池當(dāng)中;別小看這句spring作者寫的javadoc背后的意義,其實(shí)這里有兩重意思;要搞清楚這兩重意思首先知道當(dāng)代碼執(zhí)行到這里的時(shí)候其實(shí)是spring在初始化的時(shí)候執(zhí)行過來的;既然spring在初始化的時(shí)候他肯定知道這個(gè)類X.java肯定沒有在容器當(dāng)中,為什么還需要去驗(yàn)證一下呢?
好比說你第一次去天上人間,你幾乎都能確定這是你一次去你不可能跑到那里問一下前臺你有沒有辦會員吧?但是spring確這樣做了,他問了,他問問自己有沒有辦會員;為什么呢?回到你自己,如果你去問自己有沒有辦會員無非就是怕別人拿著你的身份證去辦了一個(gè)會員,或者各種原因陰差陽錯(cuò)別人吧身份證名字寫錯(cuò)了,導(dǎo)致你成了天上人間的會員;其實(shí)spring也是這個(gè)意思,因?yàn)橐粋€(gè)bean被put到單例池的渠道有很多;除了spring容器初始化—掃描類----實(shí)例化-----put到容器這條線之外還有很多方法可以把一個(gè)對象put到單例池;我這里只列舉一種,其他的有機(jī)會再討論,看下圖注意注釋:

這就相當(dāng)于在你第一次抱著緊張心態(tài)去天上人間的時(shí)候,發(fā)現(xiàn)你朋友以前拿著你的身份證去那里辦了一個(gè)會員卡一樣;
所以上面提到的這句注釋的兩重意思①第一重意思判斷spring當(dāng)前正準(zhǔn)備初始化的bean有沒有提前被put到容器;
那么第二重意思是什么呢?既然這里用來做spring初始化的工作,為什么這個(gè)方法名叫做doGetBean呢?講道理應(yīng)該叫做createBean啊才合理??;
有讀者可能會說這個(gè)方法命名可能作者亂寫的,請注意spring之所以經(jīng)久不衰命名規(guī)范絕對是一個(gè)重要原因,作者是不會這么亂給方法命名的。誠然有的讀者會說討論這個(gè)的意義不大,其實(shí)博主覺得討論這個(gè)非常重要;之所這里叫做doGetBean的原因就是因?yàn)檫@個(gè)方法就是用來獲取bean的,他主要的工作不僅僅服務(wù)于spring bean的初始化;這個(gè)方法的作用不僅僅是為了spring 在初始化bean的過程中去判斷一下這個(gè)bean是否被注冊了這么簡單;筆者認(rèn)為這個(gè)方法最主要的作用是為了從容器中得到一個(gè)bean,也就是說當(dāng)我們在spring代碼中調(diào)用getBean(“a”)其背后的意義就是調(diào)用這個(gè)doGetBean,同樣用一段代碼來證明:
圖⑩ 注意這是張gif,如果你看著不動請參考我上面說的方法

可以看到當(dāng)我調(diào)用ac.getBean(“x”)的時(shí)候,底層其實(shí)就調(diào)用doGetBean獲取這X對象的;spring之所以這么設(shè)計(jì)就是因?yàn)榕袛郻ean是否初始化好和get一個(gè)bean都需要從單例池當(dāng)中獲取,所以創(chuàng)建bean和getBean都需要調(diào)用這個(gè)doGetBean方法;也就是第②重意思,這個(gè)方法其實(shí)就是程序員getBean的底層實(shí)現(xiàn);
換成天上人間,你第一次跑去前臺,人家前臺直接說:先生請出示會員卡;你可能會奇怪——我是來全套的,你應(yīng)該問我要什么服務(wù),不是問會員卡;但是人家前臺的職責(zé)有兩,辦會員和問你要什么服務(wù);所以才會說出這句話;doGetBean也是這個(gè)意思,于是解釋了這個(gè)方法名的意義了;
總結(jié)一下 Object sharedInstance = getSingleton(beanName);目前看來主要是用于在spring初始化bean的時(shí)候判斷bean是否在容器當(dāng)中;以及供程序員直接get某個(gè)bean。
注意筆者這里用了 目前這個(gè)詞;因?yàn)間etSingleton(beanName);這個(gè)方法代碼比較多;他里面的邏輯是實(shí)現(xiàn)循環(huán)依賴最主要的代碼,文章下面我會回過頭再來講這個(gè)方法的全部意義;
請注意我們當(dāng)前代碼的場景,當(dāng)前代碼是spring容器在初始化的時(shí)候,初始化X這個(gè)bean的場景;運(yùn)行到了Object sharedInstance = getSingleton(beanName);
根據(jù)上面的分析,這個(gè)時(shí)候我的X Bean肯定沒有被創(chuàng)建,所以這里返回sharedInstance = =null;
跟著解析
//deGetBean-2
//deGetBean-2
if (sharedInstance != null && args == null) {
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
由于 sharedInstance = =null 故而不會進(jìn)入這個(gè)if分支,那么什么時(shí)候不等于null呢?兩種情況1、在spring初始化完成后程序員調(diào)用getBean(“x”)的時(shí)候得到的sharedInstance 就不等于null;2、循環(huán)依賴的時(shí)候第二次獲取對象的時(shí)候這里也不等于空;比如X 依賴 Y;Y依賴X;spring做初始化第一次執(zhí)行到這里的時(shí)候X 肯定等于null,然后接著往下執(zhí)行,當(dāng)執(zhí)行到屬性注入Y的時(shí)候,Y也會執(zhí)行到這里,那么Y也是null,因?yàn)閅也沒初始化,Y也會接著往下執(zhí)行,當(dāng)Y執(zhí)行到屬性注入的時(shí)候獲取容器中獲取X,也就是第二次執(zhí)行獲取X;這個(gè)時(shí)候X則不為空;至于具體原因,讀者接著往下看;
至于這個(gè)if分支里面的代碼干了什么事情,本文不討論,放到后面寫factoryBean的時(shí)候討論,現(xiàn)在你可以理解if分支里面就把sharedInstance 原原本本的返回出來就行;即這個(gè)if分支沒有意義;
上文說了本次不進(jìn)入if分支,所以這行代碼解析完畢;
接下解析 doGetBean -3
else{
deGetBean-3
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
如果把throw刪了可能更加清晰吧,下面是刪除后的代碼
if (isPrototypeCurrentlyInCreation(beanName)) {}
不進(jìn)if分支,則進(jìn)入這個(gè)else分支,把throw刪了 就一句代碼;判斷當(dāng)前初始化的bean----X 是不是正在創(chuàng)建原型bean集合當(dāng)中當(dāng)中?
spring源碼當(dāng)中關(guān)于這行代碼有兩行javadoc:

比較簡單我就不翻譯了,一般情況下這里返回false,也就是不會進(jìn)入if分支拋異常;為什么說一般情況下呢?首先這里是判斷當(dāng)前的類是不是正在創(chuàng)建的原型集合當(dāng)中,即里面只會存原型;一般情況下我們的類不是原型,而是單例的,大家都知道spring默認(rèn)是單例;所以返回false,再就是即使這個(gè)bean是原型也很少會在這里就存在正在創(chuàng)建的原型集合當(dāng)中。
因?yàn)椴还軉卫€是原型,bean在創(chuàng)建的過程中會add到這個(gè)集合當(dāng)中,但是創(chuàng)建完成之后就會從這個(gè)集合remove掉(關(guān)于這個(gè)文章后面有證明),原型情況第一次創(chuàng)建的時(shí)候會add到這個(gè)集合,但是不是在這里,而是在后面的創(chuàng)建過程中add,所以這里肯定不會存在,即使后面過程中add到這個(gè)集合了,但是創(chuàng)建完成之后也會remove掉,故而下一次實(shí)例化同一個(gè)原型bean(原型可以實(shí)例化無數(shù)次)的時(shí)候當(dāng)代碼執(zhí)行到這里也不可能存在集合當(dāng)中了;除非循環(huán)依賴會在bean還沒有在這個(gè)集合remove之前再次判斷一次,才有可能會存在,故而我前面說了一般情況下這里都返回false;那么單例情況我們已經(jīng)說了一定返回false,原型情況只有循環(huán)依賴才會成立,但是只要是正常人就不會對原型對象做循環(huán)依賴的;即使你用原型做了循環(huán)依賴這里也出拋異常(因?yàn)閕f成立,進(jìn)入分支 throw exception)。再一次說明原型不支持循環(huán)依賴(當(dāng)然你非得用原型做循環(huán)依賴,其實(shí)有辦法,以后文章說明,本文忽略);
畫了一幅圖說明上面的文字,因?yàn)檫@個(gè)集合非常重要,但是讀者如果這里不理解也沒關(guān)系,文章下面我還會結(jié)合代碼分析一次。

重點(diǎn)來了:說明叫做正在創(chuàng)建的原型集合呢? 還有一個(gè)與之對應(yīng)的叫做正在創(chuàng)建的單例集合
唯一的區(qū)別就是集合里面存的是單例和原型
故而我們統(tǒng)稱正在創(chuàng)建的集合,關(guān)于正在創(chuàng)建的集合是什么我下面會解釋
但是需要記住的,這個(gè)集合是我的一家之言,說白了這是筆者自己翻譯的,叫做正在創(chuàng)建的集合,沒有官方支持,至少我也沒在書上看到過這個(gè)名詞
下面解析doGetBean-4
else{
//doGetBean-4
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
同樣把拋異常的代碼刪了,如下
//doGetBean-4
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
return createBean(beanName, mbd, args);
});
代碼有點(diǎn)多;if (mbd.isSingleton()) 比較簡單,判斷當(dāng)前bean是否單例;本文環(huán)境下是成立的;繼而
sharedInstance = getSingleton(beanName, () -> {
return createBean(beanName, mbd, args);
});
這里又調(diào)用了一次getSingleton,如果有印象上面也調(diào)用了一次getSingleton,這是方法重載,兩個(gè)getSingleton方法并不是同一個(gè)方法,讀者自己看參數(shù)就行,為了區(qū)別我這這里叫做第二次調(diào)用getSingleton;上文的叫做第一次調(diào)用getSingleton;
由于這里使用lamda表達(dá)式,有些讀者看起來不是很理解;筆者改一下吧
ObjectFactory<?> singletonFactory = new ObjectFactory(){
public Object getObject(){
//其實(shí)這是個(gè)抽象類,不能實(shí)例化
//createBean是子類實(shí)現(xiàn)的,這里就不關(guān)心了
//你就理解這不是一個(gè)抽象類吧
AbstractBeanFactory abf = new AbstractBeanFactory();
Object bean = abf.createBean(beanName, mbd, args);
return bean;
};
};
//傳入 beanName 和singletonFactory 對象
sharedInstance = getSingleton(beanName,singletonFactory);
這樣看是不是明白多了呢?
當(dāng)然第二次getSingleton就會把我們bean創(chuàng)建出來,換言之整個(gè)bean如何被初始化的都是在這個(gè)方法里面;至此本文當(dāng)中筆者例舉出來的doGetBean方法的核心代碼看起來解析完成了;
注意我說的是本文當(dāng)中例舉的doGetBean代碼,前面我已經(jīng)說了我刪了很多和循環(huán)依賴無關(guān)的代碼,實(shí)際spring源碼當(dāng)中這個(gè)方法的代碼很多,以后文章介紹吧;
接下來就要研究第二次getSingleton方法的內(nèi)容了,因?yàn)槲艺f了整個(gè)bean初始化過程都在里面體現(xiàn)了;
我先把spring源碼貼出來,讀者可以忽略這里,因?yàn)橄旅鏁喆a;之所以貼出源碼就是想告訴讀者,為了研究循環(huán)依賴,本文中的很代碼我是做了刪減的;
spring源碼:-----讀者可以忽略
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
下面是我刪減后只和循環(huán)依賴有關(guān)的代碼
public Object getSingleton(String beanName, ObjectFactory<?>
singletonFactory) {
//getSingleton2 -1
Object singletonObject = this.singletonObjects.get(beanName);
//getSingleton2 -2
if (singletonObject == null) {
//getSingleton2 -3
if (this.singletonsCurrentlyInDestruction) {
throw new Exception(beanName,
"excepition");
}
//getSingleton2 -4
beforeSingletonCreation(beanName);
//getSingleton2 -5
singletonObject = singletonFactory.getObject();
}
return singletonObject;
}
//getSingleton2 -1 開始解析
Object singletonObject = this.singletonObjects.get(beanName);
第二次getSingleton上來便調(diào)用了this.singletonObjects.get(beanName),直接從單例池當(dāng)中獲取這個(gè)對象,由于這里是創(chuàng)建故而一定返回null;singletonObjects是一個(gè)map集合,即所謂的單例池;用大白話說spring所有的單例bean實(shí)例化好都存放在這個(gè)map當(dāng)中,這也是很多讀者以前認(rèn)為的spring容器,但是筆者想說這種理解是錯(cuò)誤的,因?yàn)閟pring容器的概念比較抽象,而單例池只是spring容器的一個(gè)組件而已;但是你如果一定要找一個(gè)平衡的說法,只能說這個(gè)map——singletonObjects僅僅是狹義上的容器;比如你的原型bean便不在這個(gè)map當(dāng)中,所以是狹義的spring容器;下圖為這個(gè)map在spring源碼當(dāng)中的定義:

//getSingleton2 -2 開始解析
if (singletonObject == null) {
上面解釋了,在spring 初始化bean的時(shí)候這里肯定為空,故而成立
//getSingleton2 -3 開始解析
if (this.singletonsCurrentlyInDestruction) {
throw new Exception(beanName,
"excepition");
}
這行代碼其實(shí)比較簡單,判斷當(dāng)前實(shí)例化的bean是否正在銷毀的集合里面;spring不管銷毀還是創(chuàng)建一個(gè)bean的過程都比較繁瑣,都會先把他們放到一個(gè)集合當(dāng)中標(biāo)識正在創(chuàng)建或者銷毀;所以如果你理解了前面那個(gè)正在創(chuàng)建集合那么這個(gè)正在銷毀集合也就理解了;但是不理解也沒關(guān)系,下面會分析這些集合;
如果一個(gè)bean正在創(chuàng)建,但是有正在銷毀那么則會出異常;為什么會有這種情況?其實(shí)也很簡單,多線程可能會吧;
//getSingleton2 -4 假設(shè)解析
beforeSingletonCreation(beanName);
這段代碼就比較重要了,關(guān)于上面說那個(gè)正在創(chuàng)建和正在銷毀的集合;這段代碼就能解釋,所以如果上面你沒看明白那個(gè)集合的意義,筆者這里用spring源碼來說明一下;先看看當(dāng)代碼執(zhí)行到這里的時(shí)候語境:

當(dāng)spring覺得可以著手來創(chuàng)建bean的時(shí)候首先便是調(diào)用beforeSingletonCreation(beanName);判斷當(dāng)前正在實(shí)例化的bean是否存在正在創(chuàng)建的集合當(dāng)中,說白了就是判斷當(dāng)前是否正在被創(chuàng)建;因?yàn)閟pring不管創(chuàng)建原型bean還是單例bean,當(dāng)他需要正式創(chuàng)建bean的時(shí)候他會記錄一下這個(gè)bean正在創(chuàng)建(add到一個(gè)set集合當(dāng)中);故而當(dāng)他正式創(chuàng)建之前他要去看看這個(gè)bean有沒有正在被創(chuàng)建(是否存在集合當(dāng)中); 為什么spring要去判斷是否存在這個(gè)集合呢?原因很多除了你們能想到了(你們能想到的基本不會出現(xiàn),比如并發(fā)啊,重復(fù)創(chuàng)建什么的,因?yàn)樗呀?jīng)做了嚴(yán)格并發(fā)處理),其實(shí)這個(gè)集合主要是為了循環(huán)依賴服務(wù)的,怎么服務(wù)的呢?慢慢看吧,首先我們來看下這行代碼的具體內(nèi)容:

源碼:
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
1、this.inCreationCheckExclusions.contains(beanName)這里是判斷當(dāng)前需要?jiǎng)?chuàng)建的bean是否在Exclusions集合,被排除的bean,程序員可以提供一些bean不被spring初始化(哪怕被掃描到了,也不初始化),那么這些提供的bean便會存在這個(gè)集合當(dāng)中;一般情況下我們不會提供,而且與循環(huán)依賴無關(guān);故而所以這里不做深入分析,后面文章如果寫到做分析;
this.singletonsCurrentlyInCreation.add(beanName),如果當(dāng)前bean不在排除的集合當(dāng)中那么則這個(gè)bean添加到singletonsCurrentlyInCreation(當(dāng)然這里只是把bean名字添加到集合,為了方便我們直接認(rèn)為把bean添加到集合吧,因?yàn)樗芨鶕?jù)名字能找打?qū)?yīng)的bean);
關(guān)于singletonsCurrentlyInCreation的定義參考下圖

其實(shí)就是一個(gè)set集合,當(dāng)運(yùn)行完this.singletonsCurrentlyInCreation.add(beanName) 之后結(jié)果大概如下圖這樣

我們可以通過debug來調(diào)試證明一下上面這幅圖
注意這是張gif,如果你看著不動請參考我上面說的方法

結(jié)果分析:當(dāng)代碼運(yùn)行完this.singletonsCurrentlyInCreation.add(beanName)之后可以看到singletonsCurrentlyInCreation集合當(dāng)中只存在一個(gè)x,并且后天并沒有執(zhí)行x的構(gòu)造方法,說明spring僅僅是把x添加到正在創(chuàng)建的集合當(dāng)中,但是并沒有完成bean的創(chuàng)建(因?yàn)檫B構(gòu)造方法都沒調(diào)用);
請一定注意這個(gè)集合的數(shù)據(jù)情況(目前只有一個(gè)x);因?yàn)檫@和循環(huán)依賴有天大的關(guān)系;add完x之后代碼接著往下執(zhí)行;
//getSingleton2 -5 開始分析
singletonObject = singletonFactory.getObject();
可能有讀者已經(jīng)忘記了singletonFactory這個(gè)對象怎么來的了;筆者再把代碼貼一遍吧
ObjectFactory<?> singletonFactory = new ObjectFactory(){
public Object getObject(){
//其實(shí)這是個(gè)抽象類,不能實(shí)例化
//createBean是子類實(shí)現(xiàn)的,這里就不關(guān)心了
//你就理解這不是一個(gè)抽象類吧
AbstractBeanFactory abf = new AbstractBeanFactory();
Object bean = abf.createBean(beanName, mbd, args);
return bean;
};
};
//傳入 beanName 和singletonFactory 對象
sharedInstance = getSingleton(beanName,singletonFactory);
singletonFactory.getObject();調(diào)用的就是上面代碼中g(shù)etObject方法,換言之調(diào)用的是abf.createBean(beanName, mbd, args);把創(chuàng)建好的bean返回出來;至此第二次getSingleton方法結(jié)束,bean通過singletonFactory.getObject();調(diào)用createBean建完成;接下來分析createBean的源碼,繼續(xù)探討循環(huán)依賴的原理;
AbstractAutowireCapableBeanFactory#createBean()方法中調(diào)用了doCreateBean方法創(chuàng)建bean;下圖是dubug流程
注意這是張gif,如果你看著不動請參考我上面說的方法

結(jié)果分析:因?yàn)閳?zhí)行完doCreateBean之后X和Y的構(gòu)造方法都已經(jīng)完成了調(diào)用,說明這個(gè)方法里面對X做了實(shí)例化,也就是把bean創(chuàng)建好了,而且完成了循環(huán)依賴(因?yàn)閅的構(gòu)造方法也打印說明X在完成屬性注入的時(shí)候注入了Y,所以Y也實(shí)例化了,Y bean也創(chuàng)建好了)。
接下來重點(diǎn)分析這個(gè)doCreateBean方法內(nèi)容。
好了,文章篇幅問題,接下來的內(nèi)容,筆者再分一篇文章來分享。
另外,文章已在B站同步:子路玩Java,有興趣的可以去看看,還有一些關(guān)于Spring的視頻講解,以及Spring循環(huán)依賴的面試回答方式。
如果大家覺得筆者寫的還不錯(cuò),或者感興趣的話,可以點(diǎn)一個(gè)關(guān)注支持一下筆者!感謝大家支持!