一文搞懂 Spring 循環(huán)依賴
一 循環(huán)依賴
1.1 什么是循環(huán)依賴
首先,什么是循環(huán)依賴?這個(gè)其實(shí)好理解,就是兩個(gè) Bean 互相依賴,類似下面這樣:
@Service
public class AService {
@Autowired
BService bService;
}
@Service
public class BService {
@Autowired
AService aService;
}
AService 和 BService 互相依賴:
圖片
這個(gè)應(yīng)該很好理解。
1.2 循環(huán)依賴的類型
一般來(lái)說(shuō),循環(huán)依賴有三種不同的形態(tài),上面 1.1 小節(jié)是其中一種。
另外兩種分別是三者依賴,如下圖:
圖片
這種循環(huán)依賴一般隱藏比較深,不易發(fā)覺(jué)。
還有自我依賴,如下圖:
圖片
一般來(lái)說(shuō),如果我們的代碼中出現(xiàn)了循環(huán)依賴,則說(shuō)明我們的代碼在設(shè)計(jì)的過(guò)程中可能存在問(wèn)題,我們應(yīng)該盡量避免循環(huán)依賴的發(fā)生。不過(guò)一旦發(fā)生了循環(huán)依賴,Spring 默認(rèn)也幫我們處理好了,當(dāng)然這并不能說(shuō)明循環(huán)依賴這種代碼就沒(méi)問(wèn)題。實(shí)際上在目前最新版的 Spring 中,循環(huán)依賴是要額外開(kāi)啟的,如果不額外配置,發(fā)生了循環(huán)依賴就直接報(bào)錯(cuò)了。
另外,Spring 并不能處理所有的循環(huán)依賴,后面松哥會(huì)和大家進(jìn)行分析。
二 循環(huán)依賴解決思路
2.1 解決思路
那么對(duì)于循環(huán)依賴該如何解決呢?其實(shí)很簡(jiǎn)單,中加加入一個(gè)緩存就可以了,小伙伴們來(lái)看下面這張圖:
圖片
我們?cè)谶@里引入了一個(gè)緩存池。
當(dāng)我們需要?jiǎng)?chuàng)建 AService 的實(shí)例的時(shí)候,會(huì)首先通過(guò) Java 反射創(chuàng)建出來(lái)一個(gè)原始的 AService,這個(gè)原始 AService 可以簡(jiǎn)單理解為剛剛 new 出來(lái)(實(shí)際是剛剛通過(guò)反射創(chuàng)建出來(lái))還沒(méi)設(shè)置任何屬性的 AService,此時(shí),我們把這個(gè) AService 先存入到一個(gè)緩存池中。
接下來(lái)我們就需要給 AService 的屬性設(shè)置值了,同時(shí)還要處理 AService 的依賴,這時(shí)我們發(fā)現(xiàn) AService 依賴 BService,那么就去創(chuàng)建 BService 對(duì)象,結(jié)果創(chuàng)建 BService 的時(shí)候,發(fā)現(xiàn) BService 依賴 AService,那么此時(shí)就先從緩存池中取出來(lái) AService 先用著,然后繼續(xù) BService 創(chuàng)建的后續(xù)流程,直到 BService 創(chuàng)建完成后,將之賦值給 AService,此時(shí) AService 和 BService 就都創(chuàng)建完成了。
可能有小伙伴會(huì)說(shuō),BService 從緩存池中拿到的 AService 是一個(gè)半成品,并不是真正的最終的 AService,但是小伙伴們要知道,咱們 Java 是引用傳遞(也可以認(rèn)為是值傳遞,只不過(guò)這個(gè)值是內(nèi)存地址),BService 當(dāng)時(shí)拿到的是 AService 的引用,說(shuō)白了就是一塊內(nèi)存地址而已,根據(jù)這個(gè)地址找到的就是 AService,所以,后續(xù)如果 AService 創(chuàng)建完成后,BService 所拿到的 AService 就是完整的 AService 了。
那么上面提到的這個(gè)緩存池,在 Spring 容器中有一個(gè)專門的名字,就叫做 earlySingletonObjects,這是 Spring 三級(jí)緩存中的二級(jí)緩存,這里保存的是剛剛通過(guò)反射創(chuàng)建出來(lái)的 Bean,這些 Bean 還沒(méi)有經(jīng)歷過(guò)完整生命周期,Bean 的屬性可能都還沒(méi)有設(shè)置,Bean 需要的依賴都還沒(méi)有注入進(jìn)來(lái)。另外兩級(jí)緩存分別是:
- singletonObjects:這是一級(jí)緩存,一級(jí)緩存中保存的是所有經(jīng)歷了完整生命周期的 Bean,即一個(gè) Bean 從創(chuàng)建、到屬性賦值、到各種處理器的執(zhí)行等等,都經(jīng)歷過(guò)了,就存到 singletonObjects 中,當(dāng)我們需要獲取一個(gè) Bean 的時(shí)候,首先會(huì)去一級(jí)緩存中查找,當(dāng)一級(jí)緩存中沒(méi)有的時(shí)候,才會(huì)考慮去二級(jí)緩存。
- singletonFactories:這是三級(jí)緩存。在一級(jí)緩存和二級(jí)緩存中,緩存的 key 是 beanName,緩存的 value 則是一個(gè) Bean 對(duì)象,但是在三級(jí)緩存中,緩存的 value 是一個(gè) Lambda 表達(dá)式,通過(guò)這個(gè) Lambda 表達(dá)式可以創(chuàng)建出來(lái)目標(biāo)對(duì)象的一個(gè)代理對(duì)象。
有的小伙伴可能會(huì)覺(jué)得奇怪,按照上文的介紹,一級(jí)緩存和二級(jí)緩存就足以解決循環(huán)依賴了,為什么還冒出來(lái)一個(gè)三級(jí)緩存?那就得考慮 AOP 的情況了!
2.2 存在 AOP 怎么辦
上面松哥給大家介紹的是普通的 Bean 創(chuàng)建,那確實(shí)沒(méi)有問(wèn)題。但是 Spring 中還有一個(gè)非常重要的能力,那就是 AOP。
說(shuō)到這里,我得先和小伙伴么說(shuō)一說(shuō) Spring 中 AOP 的創(chuàng)建流程。
正常來(lái)說(shuō)是我們首先通過(guò)反射獲取到一個(gè) Bean 的實(shí)例,然后就是給這個(gè) Bean 填充屬性,屬性填充完畢之后,接下來(lái)就是執(zhí)行各種 BeanPostProcessor 了,如果這個(gè) Bean 中有需要代理的方法,那么系統(tǒng)就會(huì)自動(dòng)配置對(duì)應(yīng)的后置處理器,松哥舉一個(gè)簡(jiǎn)單例子,假設(shè)我有如下一個(gè) Service:
@Service
public class UserService {
@Async
public void hello() {
System.out.println("hello>>>"+Thread.currentThread().getName());
}
}
那么系統(tǒng)就會(huì)自動(dòng)提供一個(gè)名為 AsyncAnnotationBeanPostProcessor 的處理器,在這個(gè)處理器中,系統(tǒng)會(huì)生成一個(gè)代理的 UserService 對(duì)象,并用這個(gè)對(duì)象代替原本的 UserService。
那么小伙伴們要搞清楚的是,原本的 UserService 和新生成的代理的 UserService 是兩個(gè)不同的對(duì)象,占兩塊不同的內(nèi)存地址?。。?/p>
我們?cè)賮?lái)回顧下面這張圖:
圖片
如果 AService 最終是要生成一個(gè)代理對(duì)象的話,那么 AService 存到緩存池的其實(shí)還是原本的 AService,因?yàn)榇藭r(shí)還沒(méi)到處理 AOP 那一步(要先給各個(gè)屬性賦值,然后才是 AOP 處理),這就導(dǎo)致 BService 從緩存池里拿到的 AService 是原本的 AService,等到 BService 創(chuàng)建完畢之后,AService 的屬性賦值才完成,接下來(lái)在 AService 后續(xù)的創(chuàng)建流程中,AService 會(huì)變成了一個(gè)代理對(duì)象了,不是緩存池里的 AService 了,最終就導(dǎo)致 BService 所依賴的 AService 和最終創(chuàng)建出來(lái)的 AService 不是同一個(gè)。
為了解決這個(gè)問(wèn)題,Spring 引入了三級(jí)緩存 singletonFactories。
singletonFactories 的工作機(jī)制是這樣的(假設(shè) AService 最終是一個(gè)代理對(duì)象):
當(dāng)我們創(chuàng)建一個(gè) AService 的時(shí)候,通過(guò)反射剛把原始的 AService 創(chuàng)建出來(lái)之后,先去判斷當(dāng)前一級(jí)緩存中是否存在當(dāng)前 Bean,如果不存在,則:
- 首先向三級(jí)緩存中添加一條記錄,記錄的 key 就是當(dāng)前 Bean 的 beanName,value 則是一個(gè) Lambda 表達(dá)式 ObjectFactory,通過(guò)執(zhí)行這個(gè) Lambda 可以給當(dāng)前 AService 生成代理對(duì)象。
- 然后如果二級(jí)緩存中存在當(dāng)前 AService Bean,則移除掉。
現(xiàn)在繼續(xù)去給 AService 各個(gè)屬性賦值,結(jié)果發(fā)現(xiàn) AService 需要 BService,然后就去創(chuàng)建 BService,創(chuàng)建 BService 的時(shí)候,發(fā)現(xiàn) BService 又需要用到 AService,于是就先去一級(jí)緩存中查找是否有 AService,如果有,就使用,如果沒(méi)有,則去二級(jí)緩存中查找是否有 AService,如果有,就使用,如果沒(méi)有,則去三級(jí)緩存中找出來(lái)那個(gè) ObjectFactory,然后執(zhí)行這里的 getObject 方法,這個(gè)方法在執(zhí)行的過(guò)程中,會(huì)去判斷是否需要生成一個(gè)代理對(duì)象,如果需要就生成代理對(duì)象返回,如果不需要生成代理對(duì)象,則將原始對(duì)象返回即可。最后,把拿到手的對(duì)象存入到二級(jí)緩存中以備下次使用,同時(shí)刪除掉三級(jí)緩存中對(duì)應(yīng)的數(shù)據(jù)。這樣 AService 所依賴的 BService 就創(chuàng)建好了。
接下來(lái)繼續(xù)去完善 AService,去執(zhí)行各種后置的處理器,此時(shí),有的后置處理器想給 AService 生成代理對(duì)象,發(fā)現(xiàn) AService 已經(jīng)是代理對(duì)象了,就不用生成了,直接用已有的代理對(duì)象去代替 AService 即可。
至此,AService 和 BService 都搞定。
本質(zhì)上,singletonFactories 是把 AOP 的過(guò)程提前了。
2.3 小結(jié)
總的來(lái)說(shuō),Spring 解決循環(huán)依賴把握住兩個(gè)關(guān)鍵點(diǎn):
- 提前暴露:剛剛創(chuàng)建好的對(duì)象還沒(méi)有進(jìn)行任何賦值的時(shí)候,將之暴露出來(lái)放到緩存中,供其他 Bean 提前引用(二級(jí)緩存)。
- 提前 AOP:A 依賴 B 的時(shí)候,去檢查是否發(fā)生了循環(huán)依賴(檢查的方式就是將正在創(chuàng)建的 A 標(biāo)記出來(lái),然后 B 需要 A,B 去創(chuàng)建 A 的時(shí)候,發(fā)現(xiàn) A 正在創(chuàng)建,就說(shuō)明發(fā)生了循環(huán)依賴),如果發(fā)生了循環(huán)依賴,就提前進(jìn)行 AOP 處理,處理完成后再使用(三級(jí)緩存)。
原本 AOP 這個(gè)過(guò)程是屬性賦完值之后,再由各種后置處理器去處理 AOP 的(AbstractAutoProxyCreator),但是如果發(fā)生了循環(huán)依賴,就先 AOP,然后屬性賦值,最后等到后置處理器執(zhí)行的時(shí)候,就不再做 AOP 的處理了。
不過(guò)需要注意,三級(jí)緩存并不能解決所有的循環(huán)依賴。
嚴(yán)格來(lái)說(shuō),其實(shí)也不是解決不了,所有問(wèn)題都有辦法解決,只是還需要額外配置。
三 特殊情況
根據(jù)前面介紹的思路,以下一些循環(huán)依賴場(chǎng)景無(wú)法解決。
3.1 基于構(gòu)造器注入
如果依賴的對(duì)象是基于構(gòu)造器注入的,那么執(zhí)行的時(shí)候就會(huì)報(bào)錯(cuò),代碼如下:
@Service
public class AService {
BService bService;
public AService(BService bService) {
this.bService = bService;
}
}
@Service
public class BService {
AService aService;
public BService(AService aService) {
this.aService = aService;
}
}
運(yùn)行時(shí)報(bào)錯(cuò)如下:
圖片
原因分析:
前面我們說(shuō)解決循環(huán)依賴的思路是加入緩存,如下圖:
圖片
我們說(shuō)先把 AService 原始對(duì)象創(chuàng)建出來(lái),存入到緩存池中,然后再處理 AService 中需要注入的外部 Bean 等等,但是,如果 AService 依賴的 BService 是通過(guò)構(gòu)造器注入的,那就會(huì)導(dǎo)致在創(chuàng)建 AService 原始對(duì)象的時(shí)候就需要用到 BService,去創(chuàng)建 BService 時(shí)候又需要 AService,這樣就陷入到死循環(huán)了,對(duì)于這樣的循環(huán)依賴執(zhí)行時(shí)候就會(huì)出錯(cuò)。
更進(jìn)一步,如果我們?cè)?AService 中是通過(guò) @Autowired 來(lái)注入 BService 的,那么應(yīng)該是可以運(yùn)行的,代碼如下:
@Service
public class AService {
@Autowired
BService bService;
}
@Service
public class BService {
AService aService;
public BService(AService aService) {
this.aService = aService;
}
}
上面這段代碼,AService 的原始對(duì)象就可以順利創(chuàng)建出來(lái)放到緩存池中,BService 創(chuàng)建所需的 AService 也就能從緩存中獲取到,所以就可以執(zhí)行了。
3.2 prototype 對(duì)象
循環(huán)依賴雙方 scope 都是 prototype 的話,也會(huì)循環(huán)依賴失敗,代碼如下:
@Service
@Scope("prototype")
public class AService {
@Autowired
BService bService;
}
@Service
@Scope("prototype")
public class BService {
@Autowired
AService aService;
}
這種循環(huán)依賴運(yùn)行時(shí)也會(huì)報(bào)錯(cuò),報(bào)錯(cuò)信息如下(跟前面報(bào)錯(cuò)信息一樣):
圖片
原因分析:
scope 為 prototype 意思就是說(shuō)這個(gè) Bean 每次需要的時(shí)候都現(xiàn)場(chǎng)創(chuàng)建,不用緩存里的。那么 AService 需要 BService,所以就去現(xiàn)場(chǎng)創(chuàng)建 BService,結(jié)果 BService 又需要 AService,繼續(xù)現(xiàn)場(chǎng)創(chuàng)建,AService 又需要 BService...,所以最終就陷入到死循環(huán)了。
3.3 @Async
帶有 @Async 注解的 Bean 產(chǎn)生循環(huán)依賴,代碼如下:
@Service
public class AService {
@Autowired
BService bService;
@Async
public void hello() {
}
}
@Service
public class BService {
@Autowired
AService aService;
}
報(bào)錯(cuò)信息如下:
圖片
其實(shí)大家從這段報(bào)錯(cuò)信息中也能看出來(lái)個(gè)七七八八:在 BService 中注入了 AService 的原始對(duì)象,但是 AService 在后續(xù)的處理流程中被 AOP 代理了,產(chǎn)生了新的對(duì)象,導(dǎo)致 BService 中的 AService 并不是最終的 AService,所以就出錯(cuò)了!
那有小伙伴要問(wèn)了,前面我們不是說(shuō)了三級(jí)緩存就是為了解決 AOP 問(wèn)題嗎,為什么這里發(fā)生了 AOP 卻無(wú)法解決?
如下兩個(gè)前置知識(shí)大家先理解一下:
第一:
其實(shí)大部分的 AOP 循環(huán)依賴是沒(méi)有問(wèn)題的,這個(gè) @Async 只是一個(gè)特例,特別在哪里呢?一般的 AOP 都是由 AbstractAutoProxyCreator 這個(gè)后置處理器來(lái)處理的,通過(guò)這個(gè)后置處理器生成代理對(duì)象,AbstractAutoProxyCreator 后置處理器是 SmartInstantiationAwareBeanPostProcessor 接口的子類,并且 AbstractAutoProxyCreator 后置處理器重寫了 SmartInstantiationAwareBeanPostProcessor 接口的 getEarlyBeanReference 方法;而 @Async 是由 AsyncAnnotationBeanPostProcessor 來(lái)生成代理對(duì)象的,AsyncAnnotationBeanPostProcessor 也是 SmartInstantiationAwareBeanPostProcessor 的子類,但是卻沒(méi)有重寫 getEarlyBeanReference 方法,默認(rèn)情況下,getEarlyBeanReference 方法就是將傳進(jìn)來(lái)的 Bean 原封不動(dòng)的返回去。
第二:
在 Bean 初始化的時(shí)候,Bean 創(chuàng)建完成后,后面會(huì)執(zhí)行兩個(gè)方法:
- populateBean:這個(gè)方法是用來(lái)做屬性填充的。
- initializeBean:這個(gè)方法是用來(lái)初始化 Bean 的實(shí)例,執(zhí)行工廠回調(diào)、init 方法以及各種 BeanPostProcessor。
大家先把這兩點(diǎn)搞清楚,然后我來(lái)跟大家說(shuō)上面代碼的執(zhí)行流程。
- 首先 AService 初始化,初始化完成之后,存入到三級(jí)緩存中。
- 執(zhí)行 populateBean 方法進(jìn)行 AService 的屬性填充,填充時(shí)發(fā)現(xiàn)需要用到 BService,于是就去初始化 BService。
- 初始化 BService 發(fā)現(xiàn)需要用到 AService,于是就去緩存池中找,找到之后拿來(lái)用,但是!??!這里找到的 AService 不是代理對(duì)象,而是原始對(duì)象。因?yàn)樵谌?jí)緩存中保存的 AService 的那個(gè) ObjectFactory 工廠,在對(duì) AService 進(jìn)行提前 AOP 的時(shí)候,執(zhí)行的是 SmartInstantiationAwareBeanPostProcessor 類型的后置處理器 中的 getEarlyBeanReference 方法,如果是普通的 AOP,調(diào)用 getEarlyBeanReference 方法最終會(huì)觸發(fā)提前 AOP,但是,這里執(zhí)行的是 AsyncAnnotationBeanPostProcessor 中的 getEarlyBeanReference 方法,該方法只是返回了原始的 Bean,并未做任何額外處理。
- 當(dāng) BService 創(chuàng)建完成后,AService 繼續(xù)初始化,繼續(xù)執(zhí)行 initializeBean 方法。
- 在 initializeBean 方法中,執(zhí)行其他的各種后置處理器,包括 AsyncAnnotationBeanPostProcessor,此時(shí)調(diào)用的是 AsyncAnnotationBeanPostProcessor 的 postProcessAfterInitialization 方法,在該方法中為 AService 生成了代理對(duì)象。
- 在 initializeBean 方法執(zhí)行完成之后,AService 會(huì)繼續(xù)去檢查最終的 Bean 是不是還是一開(kāi)始的 Bean,如果不是,就去檢查當(dāng)前 Bean 有沒(méi)有被其他 Bean 引用過(guò),如果被引用過(guò),就會(huì)拋出來(lái)異常,也就是上圖大家看到的異常信息。
這就是松哥和大家分享的三種 Spring 默認(rèn)無(wú)法解決的循環(huán)依賴,其實(shí)也不是無(wú)法解決,需要一些額外配置也能解決。
那么對(duì)于以上問(wèn)題該如何解決?
Spring 里邊提供了辦法來(lái)解決,但是似乎又沒(méi)有解決,繼續(xù)看你就明白了。
四 @Lazy
前面提到的三種無(wú)法自動(dòng)解決的循環(huán)依賴,都可以通過(guò)添加 @Lazy 注解來(lái)解決。
如果是構(gòu)造器注入,如下:
@Service
public class AService {
BService bService;
@Lazy
public AService(BService bService) {
this.bService = bService;
}
public BService getbService() {
return bService;
}
}
@Service
public class BService {
AService aService;
@Lazy
public BService(AService aService) {
this.aService = aService;
}
public AService getaService() {
return aService;
}
}
@Lazy 注解可以添加在 AService 或者 BService 的構(gòu)造方法上,也可以都添加上。
添加上之后,我們?cè)偃?dòng)項(xiàng)目,就不會(huì)報(bào)錯(cuò)了。這樣看起來(lái)問(wèn)題解決了,但是其實(shí)還是差點(diǎn)意思,小伙伴們看一下我的啟動(dòng)代碼:
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("aop.xml");
AService aService = ctx.getBean(AService.class);
BService bService = ctx.getBean(BService.class);
System.out.println("aService.getClass() = " + aService.getClass());
System.out.println("bService.getClass() = " + bService.getClass());
System.out.println("aService.getbService().getClass() = " + aService.getbService().getClass());
System.out.println("bService.getaService().getClass() = " + bService.getaService().getClass());
最終打印結(jié)果如下:
圖片
小伙伴們看到,我們從容器中獲取到的 AService 和 BService 的 Bean 都是正常的未被代理的對(duì)象,事實(shí)上我們的原始代碼確實(shí)也沒(méi)有需要代理的地方。但是,AService 中的 BService 以及 BService 中的 AService 卻都是代理對(duì)象,按理說(shuō) AService 中的 BService 應(yīng)該和我們從 Spring 容器中獲取到的 BService 一致,BService 中的 AService 也應(yīng)該和 Spring 容器中獲取到的 AService 一致,但實(shí)際上,兩者卻并不相同。
不過(guò)這樣也好懂了,為什么 Spring 能把一個(gè)死結(jié)給解開(kāi),就是因?yàn)?AService 和 BService 各自注入的 Bean 都不是原始的 Bean,都是一個(gè)代理的 Bean,AService 中注入的 BService 是一個(gè)代理對(duì)象,同理,BService 中注入的 AService 也是一個(gè)代理對(duì)象。
這也是為什么我一開(kāi)始說(shuō)這個(gè)問(wèn)題 Spring 解決了又沒(méi)解決。
其實(shí),這就是 @Lazy 這個(gè)注解的工作原理,看名字,加了該注解的對(duì)象會(huì)被延遲加載,實(shí)際上被該注解標(biāo)記的對(duì)象,會(huì)自動(dòng)生成一個(gè)代理對(duì)象。
前面提到的另外兩個(gè)問(wèn)題,也可以通過(guò) @Lazy 注解來(lái)解決,代碼如下:
@Service
@Scope("prototype")
public class AService {
@Lazy
@Autowired
BService bService;
}
@Service
@Scope("prototype")
public class BService {
@Lazy
@Autowired
AService aService;
}
這里 @Lazy 只要一個(gè)其實(shí)就能解決問(wèn)題,也可以兩個(gè)都添加。
對(duì)于含有 @Async 注解的情況,也可以通過(guò) @Lazy 注解來(lái)解決:
@Service
public class AService {
@Autowired
@Lazy
BService bService;
@Async
public void hello() {
bService.hello();
}
public BService getbService() {
return bService;
}
}
@Service
public class BService {
@Autowired
AService aService;
public void hello() {
System.out.println("xxx");
}
public AService getaService() {
return aService;
}
}
如此,循環(huán)依賴可破!
總而言之一句話,@Lazy 注解是通過(guò)建立一個(gè)中間代理層,來(lái)破解循環(huán)依賴的。
2. 原理分析
接下來(lái)我們?cè)賮?lái)簡(jiǎn)單分析一下 @Lazy 注解處理的源碼。
先來(lái)回顧一下屬性注入的過(guò)程:
- 在創(chuàng)建 Bean 的時(shí)候,原始 Bean 創(chuàng)建出來(lái)之后,會(huì)調(diào)用 populateBean 方法進(jìn)行 Bean 的屬性填充。
- 接下來(lái)調(diào)用 postProcessAfterInstantiation 方法去判斷是否需要執(zhí)行后置處理器,如果不需要,就直接返回了。
- 調(diào)用 postProcessProperties 方法,去觸發(fā)各種后置處理器的執(zhí)行。
- 在第 3 步的方法中,調(diào)用 findAutowiringMetadata,這個(gè)方法又會(huì)進(jìn)一步觸發(fā) buildAutorwiringMetadata 方法,去找到包含了 @Autowired、@Value 以及 @Inject 注解的屬性或者方法,并將之封裝為 InjectedElement 返回。
- 調(diào)用 InjectedElement#inject 方法進(jìn)行屬性注入。
- 接下來(lái)執(zhí)行 resolvedCachedArgument 方法嘗試從緩存中找到需要的 Bean 對(duì)象。
- 如果緩存中不存在,則調(diào)用 resolveFieldValue 方法去容器中找到 Bean。
- 最后調(diào)用 makeAccessible 和 set 方法完成屬性的賦值。
在第 7 步中,調(diào)用 resolveFieldValue 方法去解析 Bean,@Lazy 注解的相關(guān)邏輯就是在這個(gè)方法中進(jìn)行處理的(對(duì)應(yīng) 最新版 Spring 源碼視頻教程)。
resolveFieldValue 方法最終會(huì)執(zhí)行到 resolveDependency 方法:
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
if (Optional.class == descriptor.getDependencyType()) {
return createOptionalDependency(descriptor, requestingBeanName);
}
else if (ObjectFactory.class == descriptor.getDependencyType() ||
ObjectProvider.class == descriptor.getDependencyType()) {
return new DependencyObjectProvider(descriptor, requestingBeanName);
}
else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
}
else {
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
在這個(gè)方法中,首先會(huì)判斷注入的屬性類型是 Optional、ObjectFactory 還是 JSR-330 中的注解,我們這里都不是,所以走最后一個(gè)分支。
在最后一個(gè) else 中,首先調(diào)用 getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary 方法看一下是否需要延遲加載 Bean 對(duì)象,@Lazy 注解就是在這里進(jìn)行處理的。如果能夠延遲加載,那么該方法的返回值就不為 null,就可以直接返回了,就不需要執(zhí)行 doResolveDependency 方法了。
ContextAnnotationAutowireCandidateResolver#getLazyResolutionProxyIfNecessary:
@Override
@Nullable
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
}
大家看一下,這個(gè)方法首先會(huì)調(diào)用 isLazy 去判斷一下是否需要延遲加載,如果需要,則調(diào)用 buildLazyResolutionProxy 方法構(gòu)建一個(gè)延遲加載的對(duì)象;如果不需要,則直接返回一個(gè) null 即可。
protected boolean isLazy(DependencyDescriptor descriptor) {
for (Annotation ann : descriptor.getAnnotations()) {
Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);
if (lazy != null && lazy.value()) {
return true;
}
}
MethodParameter methodParam = descriptor.getMethodParameter();
if (methodParam != null) {
Method method = methodParam.getMethod();
if (method == null || void.class == method.getReturnType()) {
Lazy lazy = AnnotationUtils.getAnnotation(methodParam.getAnnotatedElement(), Lazy.class);
if (lazy != null && lazy.value()) {
return true;
}
}
}
return false;
}
這個(gè)判斷方法主要是檢查當(dāng)前類中各種參數(shù)上是否含有 @Lazy 注解、方法、屬性以及類名上是否含有 @Lazy 注解,如果有,則返回 true,否則返回 false。
再來(lái)看 buildLazyResolutionProxy 方法:
private Object buildLazyResolutionProxy(
final DependencyDescriptor descriptor, final @Nullable String beanName, boolean classOnly) {
BeanFactory beanFactory = getBeanFactory();
final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;
TargetSource ts = new TargetSource() {
@Override
public Class<?> getTargetClass() {
return descriptor.getDependencyType();
}
@Override
public boolean isStatic() {
return false;
}
@Override
public Object getTarget() {
Set<String> autowiredBeanNames = (beanName != null ? new LinkedHashSet<>(1) : null);
Object target = dlbf.doResolveDependency(descriptor, beanName, autowiredBeanNames, null);
if (target == null) {
Class<?> type = getTargetClass();
if (Map.class == type) {
return Collections.emptyMap();
}
else if (List.class == type) {
return Collections.emptyList();
}
else if (Set.class == type || Collection.class == type) {
return Collections.emptySet();
}
throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
"Optional dependency not present for lazy injection point");
}
if (autowiredBeanNames != null) {
for (String autowiredBeanName : autowiredBeanNames) {
if (dlbf.containsBean(autowiredBeanName)) {
dlbf.registerDependentBean(autowiredBeanName, beanName);
}
}
}
return target;
}
@Override
public void releaseTarget(Object target) {
}
};
ProxyFactory pf = new ProxyFactory();
pf.setTargetSource(ts);
Class<?> dependencyType = descriptor.getDependencyType();
if (dependencyType.isInterface()) {
pf.addInterface(dependencyType);
}
ClassLoader classLoader = dlbf.getBeanClassLoader();
return (classOnly ? pf.getProxyClass(classLoader) : pf.getProxy(classLoader));
}
這個(gè)方法就是用來(lái)生成代理的對(duì)象的,這里構(gòu)建了代理對(duì)象 TargetSource,在其 getTarget 方法中,會(huì)去執(zhí)行 doResolveDependency 獲取到被代理的對(duì)象(doResolveDependency 的獲取邏輯可以參考 最新版 Spring 源碼視頻教程),而 getTarget 方法只有在需要的時(shí)候才會(huì)被調(diào)用。所以,@Lazy 注解所做的事情,就是在給 Bean 中的各個(gè)屬性注入值的時(shí)候,原本需要去 Spring 容器中找注入的對(duì)象,現(xiàn)在不找了,先給一個(gè)代理對(duì)象頂著,需要的時(shí)候再去 Spring 容器中查找