一、什么是三級緩存
就是在Bean生成流程中保存Bean對象三種形態(tài)的三個Map集合,如下:
// 一級緩存Map 存放完整的Bean(流程跑完的)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
// 二級緩存Map 存放不完整的Bean(只實例化完,還沒屬性賦值、初始化)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
// 三級緩存Map 存放一個Bean的lambda表達式(也是剛實例化完)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
用來解決什么問題?
這個大家應該熟知了,就是循環(huán)依賴
什么是循環(huán)依賴?
就像下面這樣,AService 中注入了BService ,而BService 中又注入了AService ,這就是循環(huán)依賴
@Service
public class AService {
@Resource
private BService bService;
}
@Service
public class BService {
@Resource
private AService aService;
}
這幾個問題我們結(jié)合源碼來一起看一下:
三級緩存分別在什么地方產(chǎn)生的?
三級緩存是怎么解決循環(huán)依賴的?
一定需要三級緩存嗎?二級緩存不行?
二、三級緩存詳解
不管你了不了解源碼,我們先看一下Bean的生成流程,看看三級緩存是在什么地方有調(diào)用,就三個地方:
- Bean實例化前會先查詢緩存,判斷Bean是否已經(jīng)存在
- Bean屬性賦值前會先向三級緩存中放入一個lambda表達式,該表達式執(zhí)行則會生成一個半成品Bean放入二級緩存
- Bean初始化完成后將完整的Bean放入一級緩存,同時清空二、三級緩存
接下來我們一個一個看!
Bean實例化前
AbstractBeanFactory.doGetBean
Bean實例化前會從緩存里面獲取Bean,防止重復實例化
DefaultSingletonBeanRegistry.getSingleton(String beanName, boolean allowEarlyReference)
我們看看這個獲取的方法邏輯:
- 從一級緩存獲取,獲取到了,則返回
- 從二級緩存獲取,獲取到了,則返回
- 從三級緩存獲取,獲取到了,則執(zhí)行三級緩存中的lambda表達式,將結(jié)果放入二級緩存,清除三級緩存
public Object getSingleton(String beanName) {
return this.getSingleton(beanName, true);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 從一級緩存中獲取Bean 獲取到了則返回 沒獲取到繼續(xù)
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
// 從二級緩存中獲取Bean 獲取到了則返回 沒獲取到則繼續(xù)
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 加一把鎖防止 線程安全 雙重獲取校驗
synchronized(this.singletonObjects) {
// 從一級緩存中獲取Bean 獲取到了則返回 沒獲取到繼續(xù)
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 從二級緩存中獲取Bean 獲取到了則返回 沒獲取到則繼續(xù)
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 從三級緩存中獲取 沒獲取到則返回
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 獲取到了 執(zhí)行三級緩存中的lambda表達式
singletonObject = singletonFactory.getObject();
// 并將結(jié)果放入二級緩存
this.earlySingletonObjects.put(beanName, singletonObject);
// 從三級緩存中移除
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
屬性賦值/注入前
AbstractAutowireCapableBeanFactory.doCreateBean
DefaultSingletonBeanRegistry.addSingletonFactory
這里就是將一個lambda表達式放入了三級緩存,我們需要去看一下這個表達式是干什么的?。?/p>
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized(this.singletonObjects) {
// 一級緩存中不存在的話
if (!this.singletonObjects.containsKey(beanName)) {
// 將lambda表達式放入三級緩存
this.singletonFactories.put(beanName, singletonFactory);
// 清除二級緩存
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
AbstractAutowireCapableBeanFactory.getEarlyBeanReference
該方法說白了就是會判斷該Bean是否需要被動態(tài)代理,兩種返回結(jié)果:
- 不需要代理,返回未屬性注入、未初始化的半成品Bean
- 需要代理,返回未屬性注入、未初始化的半成品Bean的代理對象
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
Iterator var5 = this.getBeanPostProcessors().iterator();
// 遍歷后置處理器
while(var5.hasNext()) {
BeanPostProcessor bp = (BeanPostProcessor)var5.next();
// 找到實現(xiàn)SmartInstantiationAwareBeanPostProcessor接口的
// 該接口getEarlyBeanReference方法什么時候會執(zhí)行?
// AOP動態(tài)代理的時候 該方法執(zhí)行就是判斷該Bean是否需要被代理
// 需要代理則會創(chuàng)建代理對象返回
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor)bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
// 這個Object有兩種情況,一是實例化后的半成品Bean,二是半成品Bean動態(tài)代理后的代理對象
return exposedObject;
}
注意:這里只是把lambda表達式放入了三級緩存,如果不從三級緩存中獲取,這個表達式是不執(zhí)行的,一旦執(zhí)行了,就會把半成品Bean或者半成品Bean的代理對象放入二級緩存中了
初始化后
AbstractBeanFactory.doGetBean
這里注意啊,這個getSingleton方法傳參傳了個lambda表達式,這個表達式內(nèi)部就是Bean的實例化過程,初始化完成后,是要需要執(zhí)行這個getSingleton方法的
DefaultSingletonBeanRegistry.getSingleton(beanName, singletonFactory)
這個方法與上面那個不一樣,重載了
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized(this.singletonObjects) {
// 第一次進來這里獲取肯定為null
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 省略................
try {
// 注意啊,這個就是執(zhí)行外面那個傳參的lambda表達式
// 所以這里才會跳到createBean方法那里去執(zhí)行
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
// 省略................
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
this.afterSingletonCreation(beanName);
}
// 到了這說明Bean創(chuàng)建完了
if (newSingleton) {
// 這里就會把Bean放入一級緩存中了 同時清除二、三級緩存
this.addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
DefaultSingletonBeanRegistry.addSingleton
protected void addSingleton(String beanName, Object singletonObject) {
synchronized(this.singletonObjects) {
// 放入一級緩存
this.singletonObjects.put(beanName, singletonObject);
// 清除二、三級緩存
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
總結(jié)
整個過程就三個地方跟緩存有關,我們假設現(xiàn)在要實例化A這個Bean,看看緩存是怎么變化的:
- 實例化前,獲取緩存判斷(三個緩存中肯定沒有A,獲取為null,進入實例化流程)
- 實例化完成,屬性注入前(往三級緩存中放入了一個lambda表達式,一、二級為null)
- 初始化完成(將A這個Bean放入一級緩存,清除二、三級緩存)
以上則是單個Bean生成過程中緩存的變化??!
三、怎么解決的循環(huán)依賴
上面我們把Bean流程中利用緩存的三個重要的點都找出來了,也分析了會帶來什么變化,接下來看看是怎么解決的循環(huán)依賴,我們看個圖就懂了:
以A注入B,B注入A為例:
A屬性注入前就把lambda表達式放入了第三級緩存,所以B再注入A的時候會從第三級緩存中找到A的lambda表達式并執(zhí)行,然后將半成品Bean放入第二級緩存,所以此時B注入的只是半成品的A對象,B創(chuàng)建完成后返回給A注入,A繼續(xù)初始化,完成創(chuàng)建。
注意: B注入的半成品A對象只是一個引用,所以之后A初始化完成后,B這個注入的A就隨之變成了完整的A
從上述看第三級緩存是用來提前暴露Bean對象引用的,所以解決了循環(huán)依賴,但是第二級緩存的這個半成品Bean對象干嘛的呢?
假設A同時注入了B和C,B和C又都注入了A,這時A注入B,實例化B的過程和上述是一樣的,但隨后還會注入C,那這個C在注入A的時候還會有第三級緩存用嗎?沒了吧,所以它就只能用第二級緩存的半成品Bean對象了,同樣也是引用而已
四、不用三級緩存不行嗎
可能很多小伙伴得到的答案就是不行,而且答案是因為不確定這個Bean是不是代理對象,所以搞了個lambda表達式?答案真的是這樣嗎??
我們分析一下:AOP動態(tài)代理在沒有循環(huán)依賴的時候是在哪里執(zhí)行的?Bean初始化后!有循環(huán)依賴的時候是在屬性賦值前,中間就間隔了一個屬性注入對吧,沒錯,在屬性注入的時候注入的是原始對象的引用還是代理對象的引用這個很重要,但是屬性注入會影響AOP的結(jié)果嗎?是否AOP創(chuàng)建代理對象和切面有關,和屬性注入無關,所以我們完全可以在屬性注入之前就知道這個Bean是代理對象還是非代理對象,就像下面這樣,我不將表達式放入第三級緩存了,而是直接執(zhí)行,將結(jié)果放入第二級緩存
這樣可不可以?可以吧,這樣用二級緩存就解決了,但是在一個對象沒有屬性賦值、初始化前就創(chuàng)建代理對象是有風險的!像這么做不管有沒有產(chǎn)生循環(huán)依賴,只要有AOP動態(tài)代理對象的產(chǎn)生就有一分風險,這么做是得不償失的,所以有了三級緩存,三級緩存是只有在循環(huán)依賴以及AOP動態(tài)代理同時產(chǎn)生時才會有風險。可以說是因為存在循環(huán)依賴所以被迫的導致Bean對象提前的暴露了引用!?。?/strong> 所以這下懂了吧
至于為什么多例、構(gòu)造器注入這兩種情況解決不了循環(huán)依賴就很簡單了:
循環(huán)依賴的解決原理是在對象實例化后提前暴露了引用,而這兩種情況都還沒實例化呢
五、總結(jié)
- 一級緩存:用于存儲被完整創(chuàng)建了的bean。也就是完成了初始化之后,可以直接被其他對象使用的bean。
- 二級緩存:用于存儲半成品的Bean。也就是剛實例化但是還沒有進行初始化的Bean
- 三級緩存:三級緩存存儲的是工廠對象(lambda表達式)。工廠對象可以產(chǎn)生Bean對象提前暴露的引用(半成品的Bean或者半成品的代理Bean對象),執(zhí)行這個lambda表達式,就會將引用放入二級緩存中
經(jīng)過以上的分析,現(xiàn)在應該懂了吧:
循環(huán)依賴是否一定需要三級緩存來解決? 不一定,但三級緩存會更合適,風險更小
二級緩存能否解決循環(huán)依賴? 可以,但風險比三級緩存更大
第二級緩存用來干嘛的? 存放半成品的引用,可能產(chǎn)生多對象循環(huán)依賴,第三級緩存產(chǎn)生引用后,后續(xù)的就可以直接注入該引用
多例、構(gòu)造器注入為什么不能解決循環(huán)依賴? 因為循環(huán)依賴的原理的實例化后提前暴露的引用,這兩種情況還沒實例化文章來源:http://www.zghlxwxcb.cn/news/detail-780143.html
個人博客: 全是干貨,相信不會讓你失望文章來源地址http://www.zghlxwxcb.cn/news/detail-780143.html
到了這里,關于Spring——三級緩存解決循環(huán)依賴詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!