spring解決循環(huán)依賴的方案示例
一、代碼
@Component
public class BService {
@Autowired
private AService aService;
public void work(){
System.out.println("bservice的工作");
}
}@Component
public class AService {
@Autowired
private BService bService;
public void work(){
System.out.println("aservice的工作");
}
}二、AService 的bean創(chuàng)建過(guò)程
- spring調(diào)用AService的無(wú)參構(gòu)造方法實(shí)例化得到AService類得一個(gè)aService對(duì)象。
- spring通過(guò)依賴注入填充aService中的bservice屬性。(先從單例池去找如果沒(méi)有就創(chuàng)建BService一直循環(huán)下去。。。。。
- 填充其他屬性
- 其他步驟
- 加入單例池
所以產(chǎn)生循環(huán)依賴。
三、spring三級(jí)緩存
Spring三級(jí)緩存指的是Spring框架在管理Bean時(shí)所維護(hù)的三級(jí)緩存機(jī)制,其作用是提高Bean的創(chuàng)建效率和管理效率。
1、singletonObjects:該緩存中緩存的是完全創(chuàng)建好的單例Bean,即在第二級(jí)緩存(factoryBeanInstanceCache)中返回了完整的Bean實(shí)例。
2、earlySingletonObjects:該緩存中緩存的是未完全創(chuàng)建好的單例Bean實(shí)例(即只完成了實(shí)例化和初始化部分),主要為了解決循環(huán)依賴問(wèn)題,即當(dāng)一個(gè)Bean A依賴于Bean B,而B(niǎo)ean B又依賴于Bean A時(shí),通過(guò)從earlySingletonObjects緩存中獲取到還未完成創(chuàng)建的Bean A實(shí)例,并將其注入到Bean B中,使得依賴注入操作可以順利完成。
3、singletonFactories:該緩存中緩存的是創(chuàng)建Bean的工廠,即BeanFactory的getObject()方法返回的Bean,也即Bean的創(chuàng)建過(guò)程。主要針對(duì)的是動(dòng)態(tài)代理類型的對(duì)象。
四、三級(jí)緩存的使用過(guò)程
1、獲取singletonObjects緩存中的Bean實(shí)例,如果存在則直接返回Bean對(duì)象,否則繼續(xù)操作;
2、獲取earlySingletonObjects緩存中的Bean實(shí)例,如果存在則返回Bean對(duì)象,否則繼續(xù)操作。
3、獲取singletonFactories緩存中的Bean實(shí)例(即Bean的創(chuàng)建工廠),如果存在則通過(guò)工廠方法創(chuàng)建Bean實(shí)例并保存到earlySingletonObjects緩存中、從singletonFactories緩存中移除并返回Bean實(shí)例,否則繼續(xù)操作。
當(dāng)一個(gè)Bean被創(chuàng)建完成并添加到singletonObjects緩存中后,其它依賴該Bean的Bean便可以通過(guò)getBean()方法直接獲取到完整的Bean實(shí)例,并完成依賴注入操作。
五、解決循環(huán)依賴

注意:
spring的依賴注入方式。分為setter注入和構(gòu)造器注入。spring可以解決setter類型的構(gòu)造注入,構(gòu)造器形式的注入解決不掉。
spring的生命周期可以概括為四個(gè)大階段,實(shí)例化,屬性賦值,初始化,銷毀。
六、如果只有一級(jí)緩存能否解決依賴的問(wèn)題
理論上可以,但是實(shí)際操作的時(shí)候會(huì)有問(wèn)題,一級(jí)緩存和二級(jí)緩存的區(qū)分點(diǎn),一個(gè)存放的是成品對(duì)象,一個(gè)存放的是半成品對(duì)象,當(dāng)只有一個(gè)map的時(shí)候就意味著半成品對(duì)象和成品對(duì)象放到一起,半成品對(duì)象不能夠直接暴露給外部使用,因?yàn)闀?huì)有空指針異常,所以如果非要用一個(gè)map存儲(chǔ)就要添加一個(gè)標(biāo)識(shí),來(lái)標(biāo)注是半成品對(duì)象還是成品對(duì)象。如果按照這樣方式設(shè)計(jì)代碼,會(huì)很不優(yōu)雅,所以可以直接用兩個(gè)map來(lái)解決.不需要一個(gè)。
七、如果只有二級(jí)級(jí)緩存能否解決依賴的問(wèn)題。
理論上可以,但是前提是在創(chuàng)建對(duì)象中不能有代理對(duì)象。
八、為什么必須要有三級(jí)緩存來(lái)解決循環(huán)依賴問(wèn)題?為什么三級(jí)緩存可以解決帶有代理對(duì)象的循環(huán)依賴問(wèn)題
1、同一個(gè)容器中能否出現(xiàn)同名的不同對(duì)象。
不能
2、如果出現(xiàn)了同名的不同對(duì)象,應(yīng)該怎么辦。 比如剛開(kāi)始創(chuàng)建出原始對(duì)象,后續(xù)創(chuàng)建出了代理對(duì)象。
如果在創(chuàng)建過(guò)程中出現(xiàn)了同名的不同對(duì)象,那么后面創(chuàng)建的對(duì)象會(huì)覆蓋前面所創(chuàng)建的對(duì)象。
3、為什么要使用lambda表達(dá)式這樣的方式,或者為什么要加入三級(jí)緩存呢?
對(duì)象的屬性的賦值是在 populateBean方法完成的
代理對(duì)象的創(chuàng)建是在BeanPostProcessor的后置處理方法里面完成的。
public interface BeanPostProcessor {
// 注意這個(gè)方法名稱關(guān)鍵的是before這個(gè)單詞
Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException;
// 注意這個(gè)方法名稱關(guān)鍵的是after這個(gè)單詞
Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException;
}populateBean()要比BeanPostProcessor的后置方法先執(zhí)行也就是說(shuō)
在進(jìn)行對(duì)象屬性賦值的時(shí)候,代理對(duì)象還沒(méi)有創(chuàng)建出來(lái),那么屬性的賦值只能是原始對(duì)象而在后續(xù)的步驟中又創(chuàng)建出了代理對(duì)象,此時(shí)的代理對(duì)象會(huì)有賦值的過(guò)程嗎?不會(huì),所以會(huì)出現(xiàn)一個(gè)錯(cuò)誤
this means that said other beans do not use the final version of the bean
就是說(shuō)賦值是原始對(duì)象,而最終留下來(lái)的是代理對(duì)象,所以導(dǎo)致沒(méi)有使用最終版本的bean對(duì)象。
如何解決?
將代理對(duì)象的創(chuàng)建過(guò)程提前執(zhí)行,也就是說(shuō)在進(jìn)行對(duì)象賦值的時(shí)候必須要唯一性的確定出到底是原始對(duì)象還是代理對(duì)象,這個(gè)方法是在getEarlyBeanReference方法里執(zhí)行的,而getEarlyBeanReference是在populateBean方法的中調(diào)用的。
為什么使用lambda表達(dá)式
lambda相當(dāng)于延遲執(zhí)行,因?yàn)榇朔椒ù畏椒ú⒉粫?huì)在方法的調(diào)用的時(shí)候立即執(zhí)行,而是在對(duì)象必須要進(jìn)行屬性賦值的那一刻執(zhí)行,也就是說(shuō)在對(duì)象賦值的的那一刻確定出了最終的bean對(duì)象。
總結(jié):使用三級(jí)緩存本質(zhì)上是為了解決aop代理問(wèn)題。當(dāng)一個(gè)對(duì)象需要被代理的時(shí)候,在整個(gè)整個(gè)bean創(chuàng)建過(guò)程中,包含兩個(gè)對(duì)象,一個(gè)是普通對(duì)象,一個(gè)是代理生成代理對(duì)象,bean默認(rèn)都是單例的,那么在整個(gè)過(guò)程中三級(jí)緩存在getEarlyBeanReference進(jìn)行了一個(gè)判斷。如果不需要代理直接放回普通對(duì)象,如果需要代理就用代理對(duì)象替換。保證了bean的全局唯一性。所以能夠解決aop代理問(wèn)題。
以上就是spring解決循環(huán)依賴的方案示例的詳細(xì)內(nèi)容,更多關(guān)于spring解決循環(huán)依賴的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Springboot過(guò)濾器禁止ip頻繁訪問(wèn)功能實(shí)現(xiàn)
這篇文章主要介紹了Springboot過(guò)濾器禁止ip頻繁訪問(wèn)功能實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
SpringSecurity實(shí)現(xiàn)踢出指定用戶的示例
SpringSecurity中使用SessionRegistryImpl類可以獲取session信息并踢出用戶,這篇文章主要介紹了SpringSecurity實(shí)現(xiàn)踢出指定用戶的示例,需要的朋友可以參考下2025-03-03
搜索一文入門(mén)ElasticSearch(節(jié)點(diǎn) 分片 CRUD 倒排索引 分詞)
這篇文章主要為大家介紹了搜索一文入門(mén)ElasticSearch(節(jié)點(diǎn) 分片 CRUD 倒排索引 分詞)的基礎(chǔ)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
Java Validation方法入?yún)⑿r?yàn)實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了Java Validation方法入?yún)⑿r?yàn)實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11
JAVA 數(shù)據(jù)結(jié)構(gòu)之Queue處理實(shí)例代碼
這篇文章主要介紹了JAVA 數(shù)據(jù)結(jié)構(gòu)之Queue處理實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-02-02
Java自帶消息隊(duì)列Queue的使用教程詳細(xì)講解
這篇文章主要介紹了Java自帶消息隊(duì)列Queue的使用教程,Java中的queue類是隊(duì)列數(shù)據(jù)結(jié)構(gòu)管理類,在它里邊的元素可以按照添加它們的相同順序被移除,隊(duì)列通常以FIFO的方式排序各個(gè)元素,感興趣想要詳細(xì)了解可以參考下文2023-05-05
Java中數(shù)據(jù)庫(kù)連接池HikariCP和Druid的技術(shù)對(duì)比與性能分析
在現(xiàn)代Java應(yīng)用開(kāi)發(fā)中,數(shù)據(jù)庫(kù)連接池是提升應(yīng)用性能的關(guān)鍵組件之一,本文將深入對(duì)比兩個(gè)主流的Java數(shù)據(jù)庫(kù)連接池,希望對(duì)大家有一定的幫助2025-08-08
解決Test類中不能使用Autowired注入bean的問(wèn)題
這篇文章主要介紹了解決Test類中不能使用Autowired注入bean的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09

