spring三級(jí)緩存以及為什么不用二級(jí)緩存解讀
spring三級(jí)緩存及為什么不用二級(jí)緩存
spring是如何使用三級(jí)緩存解決循環(huán)依賴(lài)的呢?
首先 我們以下討論的bean都不是多例的,多例的bean不存在緩存當(dāng)中,因此spring是無(wú)法解決多例bean的循環(huán)依賴(lài)的。
我們知道bean的初始化過(guò)程分為三步:實(shí)例化,依賴(lài)注入,初始化。
首先讓我們來(lái)看看三級(jí)緩存里面各存了些什么:
- 一級(jí)緩存:存放已經(jīng)完全初始化之后的bean,這也是我們?cè)诔绦蚶锩婺玫降腷ean
- 二級(jí)緩存:存放實(shí)例化完成,但是并沒(méi)有進(jìn)行依賴(lài)注入和初始化的bean
- 三級(jí)緩存:bean還沒(méi)有進(jìn)行實(shí)例化,單單保存了bean的創(chuàng)建工廠。
這種緩存機(jī)制能夠幫我們解決這樣一種循環(huán)依賴(lài):A在B之前進(jìn)行實(shí)例化,并且A需要setter注入B,B需構(gòu)造器注入A(或者屬性注入A)。
注意:
- 當(dāng)B獲得A的引用時(shí),可以晚一點(diǎn)設(shè)置A的屬性。
- 但是B的構(gòu)造器必須生成,不生成沒(méi)東西引用,也就是說(shuō)B一定要獲取A,即使A還沒(méi)設(shè)置屬性也可以。
- 如果B在A之前實(shí)例化,那么B就會(huì)無(wú)法引用A,就會(huì)直接報(bào)錯(cuò)。
這樣在初始化階段就會(huì)進(jìn)行以下流程:
1:A要進(jìn)行初始化,先實(shí)例化,先將A的創(chuàng)建工廠放在三級(jí)緩存中,然而A在自己的屬性注入里面發(fā)現(xiàn)B還沒(méi)有出現(xiàn),于是先去初始化B
2:B要進(jìn)行初始化,先實(shí)例化,先把B的創(chuàng)建工廠放在三級(jí)緩存種,然后B發(fā)現(xiàn)自己的屬性注入需要A,去一級(jí)找不到A,二級(jí)找不到A,在三級(jí)找到到了A的創(chuàng)建工廠,于是調(diào)用創(chuàng)建工廠的getobject()方法創(chuàng)建一個(gè)A的實(shí)例化對(duì)象(如果A實(shí)現(xiàn)了aop,那么就是創(chuàng)建的 A的代理對(duì)象),放入二級(jí)緩存并且添加入自己的構(gòu)造器/屬性(可以允許A屬性晚點(diǎn)設(shè)置,但是不能沒(méi)有),這樣B就可以完成自己的初始化,初始化完成之后放入一級(jí)緩存并且刪掉二三級(jí)B緩存
3:B的初始化完成了,A繼續(xù)屬性賦值,順利從?級(jí)緩存拿到實(shí)例化且初始化完成的B對(duì)象,A對(duì)象創(chuàng)建也完成,刪除?級(jí)緩存中的A,同時(shí)把A放??級(jí)緩存
為什么要三級(jí)緩存而不是二級(jí)?
1 避免重復(fù),如果只有1,3兩級(jí)緩存,那么當(dāng)A需要BC 而B(niǎo)C都需要A的時(shí)候,BC就會(huì)同時(shí)調(diào)用A的創(chuàng)建工廠,生成兩份A,導(dǎo)致重復(fù)創(chuàng)建
2 spring希望代理和bean的生命周期要分開(kāi),如果一開(kāi)始就直接調(diào)用創(chuàng)建工廠創(chuàng)建bean的話,那么所有擁有aop的bean就會(huì)直接進(jìn)行動(dòng)態(tài)代理,但是spring希望在bean完成初始化之后再生成最終代理。如果不分開(kāi)的話,也就是說(shuō)不需要3級(jí)緩存,1級(jí)存放bean,2級(jí)存放實(shí)例化后的bean,所有的bean直接調(diào)用創(chuàng)建工廠創(chuàng)建并且放在二級(jí)緩存,那么也是沒(méi)問(wèn)題的。
采用三級(jí)緩存,在這個(gè)過(guò)程中如果B需要A注入,而A存在代理的話,那么就會(huì)生成一個(gè)早期的代理對(duì)象給B,用于解決循環(huán)依賴(lài),而最終的代理對(duì)象還是要在beanA完成了屬性填充和初始化后再繼續(xù)進(jìn)行,這兩個(gè)生成的代理對(duì)象是存在區(qū)別的,因此也不算真正破壞到Spring代理在Bean初始化完成后的原則,因?yàn)镾pring仍然確保了最終的代理對(duì)象是在Bean完全初始化后才生成的。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
基于Gradle搭建Spring?5.3.13-release源碼閱讀環(huán)境的詳細(xì)流程
這篇文章主要介紹了基于Gradle搭建Spring?5.3.13-release源碼閱讀環(huán)境,首先安裝jdk、gradle等一系列必要操作,本文通過(guò)實(shí)例代碼相結(jié)合給大家講解的非常詳細(xì),需要的朋友可以參考下2022-04-04springSecurity之AuthenticationProvider用法解析
這篇文章主要介紹了springSecurity之AuthenticationProvider用法解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03Jersey Restful接口如何獲取參數(shù)的問(wèn)題
這篇文章主要介紹了Jersey Restful接口如何獲取參數(shù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06JAVA超級(jí)簡(jiǎn)單的爬蟲(chóng)實(shí)例講解
下面小編就為大家?guī)?lái)一篇JAVA超級(jí)簡(jiǎn)單的爬蟲(chóng)實(shí)例講解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10Spring boot整合shiro+jwt實(shí)現(xiàn)前后端分離
這篇文章主要為大家詳細(xì)介紹了Spring boot整合shiro+jwt實(shí)現(xiàn)前后端分離,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12SpringBoot無(wú)法訪問(wèn)webapp目錄下的文件問(wèn)題
這篇文章主要介紹了SpringBoot無(wú)法訪問(wèn)webapp目錄下的文件問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05