国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Spring——三級緩存解決循環(huán)依賴詳解

這篇具有很好參考價值的文章主要介紹了Spring——三級緩存解決循環(huán)依賴詳解。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

一、什么是三級緩存

就是在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)用,就三個地方:

  1. Bean實例化前會先查詢緩存,判斷Bean是否已經(jīng)存在
  2. Bean屬性賦值前會先向三級緩存中放入一個lambda表達式,該表達式執(zhí)行則會生成一個半成品Bean放入二級緩存
  3. Bean初始化完成后將完整的Bean放入一級緩存,同時清空二、三級緩存

接下來我們一個一個看!

spring三級緩存如何解決循環(huán)依賴,spring,緩存,spring,java,循環(huán)依賴

Bean實例化前

AbstractBeanFactory.doGetBean

Bean實例化前會從緩存里面獲取Bean,防止重復實例化

spring三級緩存如何解決循環(huán)依賴,spring,緩存,spring,java,循環(huán)依賴

DefaultSingletonBeanRegistry.getSingleton(String beanName, boolean allowEarlyReference)

我們看看這個獲取的方法邏輯:

  1. 從一級緩存獲取,獲取到了,則返回
  2. 從二級緩存獲取,獲取到了,則返回
  3. 從三級緩存獲取,獲取到了,則執(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

spring三級緩存如何解決循環(huán)依賴,spring,緩存,spring,java,循環(huán)依賴

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方法的

spring三級緩存如何解決循環(huán)依賴,spring,緩存,spring,java,循環(huán)依賴

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,看看緩存是怎么變化的:

  1. 實例化前,獲取緩存判斷(三個緩存中肯定沒有A,獲取為null,進入實例化流程)
  2. 實例化完成,屬性注入前(往三級緩存中放入了一個lambda表達式,一、二級為null)
  3. 初始化完成(將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

spring三級緩存如何解決循環(huán)依賴,spring,緩存,spring,java,循環(huán)依賴

從上述看第三級緩存是用來提前暴露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é)果放入第二級緩存

spring三級緩存如何解決循環(huán)依賴,spring,緩存,spring,java,循環(huán)依賴

這樣可不可以?可以吧,這樣用二級緩存就解決了,但是在一個對象沒有屬性賦值、初始化前就創(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

到了這里,關于Spring——三級緩存解決循環(huán)依賴詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • Spring使用三級緩存解決循環(huán)依賴?終于完全弄明白了

    文章閱讀前推薦 推薦先去看看源碼,源碼很短,但是對于我們在腦子里構(gòu)建一個完整思路很重要。看起來非常簡單,只需要雙擊shift,全局查找文件:AbstractAutowireCapableBeanFactory,找到550行左右的doCreateBean方法,重點看一下580行到600行這20行代碼就行,包含了三級緩存、屬性注

    2024年03月25日
    瀏覽(31)
  • Spring FrameWork從入門到NB -三級緩存解決循環(huán)依賴內(nèi)幕 (一)

    Spring FrameWork從入門到NB -三級緩存解決循環(huán)依賴內(nèi)幕 (一)

    循環(huán)依賴就是我依賴你、你依賴我,或者A依賴B、B依賴C、C依賴A…組成的錯綜復雜的依賴關系。 其實各種不同的依賴關系最終從邏輯上都可以演變?yōu)椋何乙蕾嚹?、你依賴我?循環(huán)依賴大致可以分為兩種情況: 屬性依賴:比如A對象有一個屬性B,B對象有屬性A。 構(gòu)造器依賴:

    2024年02月11日
    瀏覽(20)
  • Spring 為什么要用三級緩存來解決循環(huán)依賴(AOP),二級緩存不行嗎

    解決有代理對象的循環(huán)依賴不一定要三級緩存,用二級甚至一級也能解決,下面討論下Spring為什么選擇三級緩存這個方案。 Spring最開始是沒有三級緩存的,后面版本因為引入了AOP,有了代理對象,又因為存在循環(huán)依賴,為了保證依賴注入過程注入的是代理對象,且不完全打破

    2024年04月26日
    瀏覽(23)
  • SpringBoot 三級緩存解決循環(huán)依賴源碼分析

    SpringBoot 三級緩存解決循環(huán)依賴源碼分析

    在 SpringBoot 框架中,如果只存在兩級緩存,那么當發(fā)生循環(huán)依賴的時候可能存在異常的對象創(chuàng)建流程如下圖所示: 創(chuàng)建 A 的空白對象 a1 解析填充 A 對象的屬性,發(fā)現(xiàn)依賴的 B 對象未創(chuàng)建,則觸發(fā) B 對象創(chuàng)建 創(chuàng)建 B 對象過程中,填充對象屬性時發(fā)現(xiàn)依賴 A 對象,此時從緩存中

    2024年02月11日
    瀏覽(21)
  • Spring如何解決循環(huán)依賴問題

    循環(huán)依賴問題在Spring中主要有三種情況: (1)通過構(gòu)造方法進行依賴注入時產(chǎn)生的循環(huán)依賴問題。 (2)通過setter方法進行依賴注入且是在多例(原型)模式下產(chǎn)生的循環(huán)依賴問題。 (3)通過setter方法進行依賴注入且是在單例模式下產(chǎn)生的循環(huán)依賴問題。 在Spring中,只有第

    2024年02月06日
    瀏覽(17)
  • Spring 是如何解決循環(huán)依賴的

    Spring 是如何解決循環(huán)依賴的

    1.什么是循環(huán)依賴? 所謂的循環(huán)依賴是指,A 依賴 B,B 又依賴 A,它們之間形成了循環(huán)依賴?;蛘呤?A 依賴 B,B 依賴 C,C 又依賴 A。它們之間的依賴關系如下: 2.通過手寫代碼演示理解Spring循環(huán)依賴 DEMO: 為什么需要二級緩存? 一級緩存和二級緩存相比: 二級緩存只要是為了分

    2024年02月03日
    瀏覽(19)
  • Spring 是如何解決循環(huán)依賴問題的?

    Spring 是如何解決循環(huán)依賴問題的?

    提示:這里簡述項目相關背景: 例如:項目場景:示例:通過藍牙芯片(HC-05)與手機 APP 通信,每隔 5s 傳輸一批傳感器數(shù)據(jù)(不是很大) ????????我們都知道,如果在代碼中,將 兩個或多個 Bean 互相之間持有對方的引用 就會發(fā)生循環(huán)依賴。循環(huán)的依賴將會導致注入死循環(huán),這

    2024年02月13日
    瀏覽(23)
  • 深談Spring如何解決Bean的循環(huán)依賴

    Java循環(huán)依賴指的是兩個或多個類之間的相互依賴,形成了一個循環(huán)的依賴關系,這會導致程序編譯失敗或運行時出現(xiàn)異常。下面小岳就帶大家來詳細分析下Java循環(huán)依賴。 簡單來講就是:假設有兩個人是:A和B, A想要向B借錢,但B需要先向A借錢。 這種情況就形成了循環(huán)依賴

    2023年04月17日
    瀏覽(24)
  • Spring中的循環(huán)依賴是什么?如何解決它?

    循環(huán)依賴是指兩個或多個Bean之間相互依賴,導致它們無法被正確地初始化。在Spring中,當兩個或多個Bean之間存在循環(huán)依賴時,Spring容器無法決定哪個Bean應該先初始化,因此會拋出BeanCurrentlyInCreationException異常,從而導致應用程序啟動失敗。 ??為了解決循環(huán)依賴問題,Spr

    2023年04月16日
    瀏覽(49)
  • Spring三級緩存詳解

    Spring三級緩存 是為了解決 對象間的循環(huán)依賴 問題。 A依賴B,B依賴A,這就是一個簡單的循環(huán)依賴。 我們來先看看三級緩存的源碼。 (1)查看“獲取Bean”的源碼,注意getSingleton()方法。 (2)“添加到第1級緩存”的源碼: (3)“添加到第3級緩存”的源碼: (4)“創(chuàng)建Be

    2024年02月07日
    瀏覽(29)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包