偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

解析Spring中的循環(huán)依賴(lài)問(wèn)題:再探三級(jí)緩存(AOP)

開(kāi)發(fā) 前端
當(dāng)涉及Spring框架中動(dòng)態(tài)代理的實(shí)現(xiàn)機(jī)制時(shí),除了已經(jīng)提到的earlySingletonObjects和singletonFactories這兩個(gè)緩存外,還有一個(gè)重要的緩存值得一提,那就是earlyProxyReferences。這個(gè)緩存的作用在于記錄某個(gè)原始對(duì)象是否已經(jīng)進(jìn)行過(guò)AOP(面向切面編程)處理。

前言

在之前的內(nèi)容中,我們簡(jiǎn)要探討了循環(huán)依賴(lài),并指出僅通過(guò)引入二級(jí)緩存即可解決此問(wèn)題。然而,你可能會(huì)好奇為何在Spring框架中還需要引入三級(jí)緩存singletonFactories。在前述總結(jié)中,我已經(jīng)提供了答案,即AOP代理對(duì)象。接下來(lái),我們將深入探討這一話題。

AOP

在Spring框架中,AOP的實(shí)現(xiàn)是通過(guò)一個(gè)名為BeanPostProcessor的類(lèi)完成的,其中一個(gè)關(guān)鍵的BeanPostProcessor就是AnnotationAwareAspectJAutoProxyCreator。值得一提的是,該類(lèi)的父類(lèi)是AbstractAutoProxyCreator。在Spring的AOP機(jī)制中,通常會(huì)使用JDK動(dòng)態(tài)代理或者CGLib動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)代理對(duì)象的生成。因此,如果在某個(gè)類(lèi)的方法上設(shè)置了切面,那么最終這個(gè)類(lèi)將需要生成一個(gè)代理對(duì)象來(lái)應(yīng)用AOP的功能。

一般的執(zhí)行流程通常是這樣的:A類(lèi)--->生成一個(gè)普通對(duì)象-->屬性注入-->基于切面生成一個(gè)代理對(duì)象-->將該代理對(duì)象存入singletonObjects單例池中。

而AOP可以說(shuō)是Spring框架中除了IOC之外的另一個(gè)重要功能,而循環(huán)依賴(lài)則屬于IOC的范疇。因此,為了讓這兩個(gè)重要功能同時(shí)存在于Spring框架中,Spring需要進(jìn)行特殊處理。

三級(jí)緩存

在處理這種情況時(shí),Spring框架利用了第三級(jí)緩存singletonFactories。下面我們來(lái)看一下關(guān)于三級(jí)緩存的源代碼實(shí)現(xiàn):

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // Quick check for existing instance without full singleton lock
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                synchronized (this.singletonObjects) {
                    // Consistent creation of early reference within full singleton lock
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) {
                            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                            if (singletonFactory != null) {
                                singletonObject = singletonFactory.getObject();
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }
        return singletonObject;
    }

首先,singletonFactories中存儲(chǔ)的是某個(gè)beanName對(duì)應(yīng)的ObjectFactory。在bean的生命周期中,生成完原始對(duì)象之后,Spring框架會(huì)構(gòu)造一個(gè)ObjectFactory并將其存入singletonFactories中。這個(gè)ObjectFactory是一個(gè)函數(shù)式接口,因此支持Lambda表達(dá)式,形式為() -> getEarlyBeanReference(beanName, mbd, bean)。為了更清晰地理解這個(gè)過(guò)程,我提供一張圖片。

圖片圖片

getEarlyBeanReference

在上述Lambda表達(dá)式中,它實(shí)際上代表了一個(gè)ObjectFactory,執(zhí)行該Lambda表達(dá)式將會(huì)調(diào)用getEarlyBeanReference方法。下面是getEarlyBeanReference方法的實(shí)現(xiàn):

//AbstractAutowireCapableBeanFactory
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
            exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
        }
    }
    return exposedObject;
}

在整個(gè)Spring框架中,值得注意的是,只有AbstractAutoProxyCreator這個(gè)類(lèi)在實(shí)現(xiàn)getEarlyBeanReference方法時(shí)才具有真正的意義。這個(gè)類(lèi)專(zhuān)門(mén)用于處理AOP(面向切面編程)。

// AbstractAutoProxyCreator
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
 Object cacheKey = getCacheKey(bean.getClass(), beanName);
 this.earlyProxyReferences.put(cacheKey, bean);
 return wrapIfNecessary(bean, beanName, cacheKey);
}

那么,getEarlyBeanReference方法的具體操作是什么呢? 首先,它會(huì)獲取一個(gè)cachekey,這個(gè)cachekey實(shí)際上就是beanName。 接著,它會(huì)將beanName和bean(即原始對(duì)象)存儲(chǔ)到earlyProxyReferences中。 接下來(lái),它會(huì)調(diào)用wrapIfNecessary方法進(jìn)行AOP操作,這將生成一個(gè)代理對(duì)象。

那么,什么時(shí)候會(huì)調(diào)用getEarlyBeanReference方法呢?讓我們?cè)俅位氐窖h(huán)依賴(lài)的場(chǎng)景中。

圖片圖片

image

在我上一節(jié)的基礎(chǔ)上,我增加了兩句話,以便更好地理解觸發(fā)緩存機(jī)制以解決AOP代理對(duì)象生成的時(shí)機(jī)。

一旦原始對(duì)象通過(guò)構(gòu)造方法生成后,會(huì)被存儲(chǔ)到三級(jí)緩存中,并且會(huì)與一個(gè)lambda表達(dá)式關(guān)聯(lián)。然而,在這個(gè)階段,它并不會(huì)被執(zhí)行。

一旦BBean需要ABean時(shí),系統(tǒng)會(huì)首先查看三級(jí)緩存以確定是否存在緩存。如果存在緩存,則lambda表達(dá)式將會(huì)被執(zhí)行,其代碼已在前面展示過(guò)。該lambda表達(dá)式的目的是將代理對(duì)象放入earlySingletonObjects中。需要注意的是,此時(shí)代理對(duì)象并未被放入singletonObjects中。那么代理對(duì)象何時(shí)會(huì)被放入singletonObjects中呢?

這個(gè)時(shí)候你可能已經(jīng)明白了earlySingletonObjects的用途。由于只獲取了A原始對(duì)象的代理對(duì)象,這個(gè)代理對(duì)象并不完整,因?yàn)锳原始對(duì)象尚未進(jìn)行屬性填充。因此,在這種情況下,我們不能直接將A的代理對(duì)象放入singletonObjects中。因此,我們只能將代理對(duì)象放入earlySingletonObjects,這樣依次類(lèi)推。

在Spring框架中,在循環(huán)依賴(lài)場(chǎng)景下,當(dāng)Bean B創(chuàng)建完成后,Bean A繼續(xù)其生命周期。在Bean A完成屬性注入后,根據(jù)其自身邏輯進(jìn)行AOP操作。此時(shí),我們知道Bean A的原始對(duì)象已經(jīng)經(jīng)歷了AOP處理,因此對(duì)于Bean A本身而言,不需要再次進(jìn)行AOP。那么,如何確定一個(gè)對(duì)象是否已經(jīng)經(jīng)歷了AOP呢?

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

沒(méi)錯(cuò),這個(gè)earlyProxyReferences確實(shí)提前緩存了對(duì)象是否已經(jīng)被代理過(guò),這樣就避免了重復(fù)的AOP處理。

舉例反證

那么問(wèn)題來(lái)了,當(dāng)A對(duì)象創(chuàng)建時(shí),它可以是原始對(duì)象,但當(dāng)B對(duì)象創(chuàng)建時(shí),卻成功創(chuàng)建了A的代理對(duì)象。然后再回頭給A對(duì)象進(jìn)行屬性注入和初始化,這些操作似乎與代理對(duì)象無(wú)關(guān)?

這個(gè)問(wèn)題涉及到了 Spring 中動(dòng)態(tài)代理的實(shí)現(xiàn)。無(wú)論是使用cglib代理還是jdk動(dòng)態(tài)代理生成的代理類(lèi),代理時(shí)都會(huì)將目標(biāo)對(duì)象 target 保存在最終生成的代理 $proxy 中。你可以將代理對(duì)象看作是對(duì)原始對(duì)象地址的一層包裝,最終仍然會(huì)回到原始對(duì)象上。因此,對(duì)原始bean的進(jìn)一步完善實(shí)際上也就是對(duì)代理對(duì)象的完善。

還有一個(gè)需要注意的問(wèn)題,當(dāng)A創(chuàng)建時(shí),由于earlyProxyReferences緩存的原因,并沒(méi)有創(chuàng)建代理對(duì)象,因此此時(shí)A仍然保持為原始對(duì)象。我們知道,當(dāng)bean創(chuàng)建完成后,它將被放入一級(jí)緩存中,但如果在此之后被其他對(duì)象引用,那不就會(huì)出現(xiàn)問(wèn)題嗎?別人引用的都是原始對(duì)象了,而不是代理對(duì)象,但是請(qǐng)不要著急,因?yàn)樵趯?shí)例化之后,有一行代碼可以解決這個(gè)問(wèn)題。

if (earlySingletonExposure) {
            Object earlySingletonReference = getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                if (exposedObject == bean) {
                    exposedObject = earlySingletonReference;
                }
......省略代碼

在實(shí)例化原始對(duì)象后,他會(huì)首先從三級(jí)緩存中檢查是否存在緩存對(duì)象。這是因?yàn)樵趧?chuàng)建B對(duì)象時(shí),已經(jīng)將A的代理對(duì)象放入二級(jí)緩存。因此,取出的對(duì)象是代理對(duì)象。接著,當(dāng)進(jìn)行 exposedObject == bean 的比較時(shí),發(fā)現(xiàn)它們不相同。因此,以代理對(duì)象為準(zhǔn)并將其返回。最終,最外層存儲(chǔ)的將是代理對(duì)象。

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é)

在上一個(gè)章節(jié)中我們提到了今天要討論三級(jí)緩存,讓我們根據(jù)上面提到的三級(jí)緩存內(nèi)容,做一個(gè)詳盡的總結(jié):

  • singletonObjects:緩存經(jīng)過(guò)了完整生命周期的bean。
  • earlySingletonObjects緩存了未經(jīng)過(guò)完整生命周期的bean。當(dāng)某個(gè)bean出現(xiàn)循環(huán)依賴(lài)時(shí),該bean會(huì)被提前放入earlySingletonObjects中。如果該bean需要經(jīng)過(guò)AOP,那么代理對(duì)象將會(huì)被放入earlySingletonObjects;否則,原始對(duì)象將被放入其中。然而,無(wú)論是代理對(duì)象還是原始對(duì)象,它們的生命周期都尚未完全結(jié)束。因此視為未經(jīng)過(guò)完整生命周期的bean。
  • singletonFactories緩存的是一個(gè)ObjectFactory,這個(gè)ObjectFactory實(shí)際上是一個(gè)Lambda表達(dá)式。在每個(gè)Bean的生成過(guò)程中,當(dāng)原始對(duì)象實(shí)例化完成后,會(huì)提前基于原始對(duì)象生成一個(gè)Lambda表達(dá)式,并將其保存到三級(jí)緩存中。這個(gè)Lambda表達(dá)式可能會(huì)被使用,也可能不會(huì)。如果當(dāng)前Bean不存在循環(huán)依賴(lài),那么這個(gè)Lambda表達(dá)式將不會(huì)被使用,當(dāng)前的Bean將按照正常的生命周期執(zhí)行完畢,并將自身放入singletonObjects中。但是,如果在依賴(lài)注入的過(guò)程中發(fā)現(xiàn)了循環(huán)依賴(lài)(即當(dāng)前正在創(chuàng)建的Bean被其他Bean所依賴(lài)),則會(huì)從三級(jí)緩存中取出Lambda表達(dá)式,并執(zhí)行它以獲取一個(gè)對(duì)象,然后將得到的對(duì)象放入二級(jí)緩存中。需要特別注意的是,如果當(dāng)前Bean需要AOP處理,則執(zhí)行Lambda表達(dá)式后得到的將是代理對(duì)象;否則,直接得到的是原始對(duì)象。

當(dāng)涉及Spring框架中動(dòng)態(tài)代理的實(shí)現(xiàn)機(jī)制時(shí),除了已經(jīng)提到的earlySingletonObjects和singletonFactories這兩個(gè)緩存外,還有一個(gè)重要的緩存值得一提,那就是earlyProxyReferences。這個(gè)緩存的作用在于記錄某個(gè)原始對(duì)象是否已經(jīng)進(jìn)行過(guò)AOP(面向切面編程)處理。

至此,整個(gè)循環(huán)依賴(lài)解決完畢。

責(zé)任編輯:武曉燕 來(lái)源: 靈墨AI探索室
相關(guān)推薦

2023-12-12 17:44:13

三級(jí)緩存Bean

2022-05-08 19:23:28

Spring循環(huán)依賴(lài)

2023-02-26 11:15:42

緩存循環(huán)依賴(lài)

2024-12-20 16:46:22

Spring三級(jí)緩存

2025-06-26 01:55:00

2022-03-01 18:03:06

Spring緩存循環(huán)依賴(lài)

2022-12-02 12:01:30

Spring緩存生命周期

2021-01-29 14:14:47

動(dòng)態(tài)代理緩存

2024-04-15 08:17:21

Spring依賴(lài)注入循環(huán)依賴(lài)

2024-04-12 07:51:05

SpringBean初始化

2025-07-02 03:10:00

2021-05-06 07:58:57

Spring BeanIOCAOP

2023-05-04 08:06:27

Spring循環(huán)依賴(lài)

2024-03-18 00:00:00

SpringBean設(shè)計(jì)

2021-06-27 21:06:47

開(kāi)發(fā)循環(huán)依賴(lài)

2019-11-26 14:30:20

Spring循環(huán)依賴(lài)Java

2020-02-06 13:40:35

編程緩存優(yōu)化

2022-01-12 07:48:19

緩存Spring 循環(huán)

2009-06-12 09:00:15

Linux域名訪問(wèn)

2022-08-17 07:52:31

Spring循環(huán)依賴(lài)單例池
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)