亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺析Spring中的循環(huán)依賴問題

 更新時(shí)間:2023年11月14日 08:52:51   作者:荊軻刺秦  
這篇文章主要介紹了淺析Spring中的循環(huán)依賴問題,Spring 是利用了 三級(jí)緩存 來解決循環(huán)依賴的,其實(shí)現(xiàn)本質(zhì)是通過提前暴露已經(jīng)實(shí)例化但尚未初始化的 bean 來完成的,需要的朋友可以參考下

Spring的循環(huán)依賴

本文不會(huì)詳細(xì)講解 Spring 循環(huán)依賴的基礎(chǔ)問題。

我相信能閱讀到本文的,對(duì) Spring 循環(huán)依賴已經(jīng)有一定了解,但可能存在一些疑惑。本文就是嘗試來解決這些疑惑的。

我們都知道 Spring 是利用了 三級(jí)緩存 來解決循環(huán)依賴的,其實(shí)現(xiàn)本質(zhì)是通過提前暴露已經(jīng)實(shí)例化但尚未初始化的 bean 來完成的。

但是呢,我們?nèi)匀粫?huì)想,這里為什么要使用三級(jí)緩存?而且,我相信,不少人都曾手寫過代碼來解決循環(huán)依賴的問題,那時(shí)候,他們也只用了二級(jí)緩存,參考下圖:

循環(huán)依賴二級(jí)緩存

我們可以仔細(xì)跟蹤序號(hào),理清整個(gè)流程。所以,二級(jí)緩存是能夠解決循環(huán)依賴,這也符合它的本質(zhì):“提前暴露對(duì)象”。這個(gè)流程圖并沒有描述接下來的流程,這里使用文字簡(jiǎn)單描述下:

  • 對(duì)象A獲取到已創(chuàng)建完成的對(duì)象B注入;
  • 對(duì)象A完成字段注入以及初始化,并放入一級(jí)緩存;
  • 對(duì)象A從二級(jí)緩存中移除;

既然二級(jí)緩存能夠解決循環(huán)依賴了,那為什么要使用三級(jí)緩存呢?網(wǎng)上的說法是,那是因?yàn)?Spring 中存在替換注入對(duì)象的問題。通俗地來說就是:“一個(gè)半成品對(duì)象有可能在被對(duì)象b注入以后,被更改為其它的實(shí)例對(duì)象,那么對(duì)象b注入的就是一個(gè)過期的對(duì)象了”。

這種情況會(huì)導(dǎo)致對(duì)象b注入了一個(gè)并不存在于容器中的對(duì)象A(因?yàn)楸桓暮蟮膶?duì)象注入了容器,替換掉了原來的對(duì)象)。所以,大多數(shù)人會(huì)認(rèn)為三級(jí)緩存是為了解決這個(gè)問題的,讓我們來看看真的是如此嘛?上代碼:

@Component
public class ServiceA {
    @Autowired
    private ServiceB serviceB;

}
@Component
public class ServiceB {
    @Autowired
    private ServiceA serviceA;

}
@Component
public class ResetServiceABeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(beanName.equals("serviceA")){
            return new ServiceA();
        }
        return bean;
    }
}

ServiceA 和 ServiceB 互相依賴,ResetServiceABeanPostProcessor 則是為了在ServiceB 注入了原來的ServiceA 后,將原來的 ServiceA 給替換掉。我們模擬了上述場(chǎng)景,但最后的運(yùn)行結(jié)果卻是得到了一個(gè) BeanCurrentlyInCreationException 異常,異常在圖中的 620 行拋出。

異常拋出點(diǎn)

可以發(fā)現(xiàn),似乎它并沒有解決這個(gè)“注入了過期對(duì)象”的問題,可是它至少檢測(cè)出了這個(gè)問題。所以,我個(gè)人認(rèn)為,三級(jí)緩存并不是來解決這個(gè)問題,而是來在啟動(dòng)時(shí)檢測(cè)這個(gè)問題的。

文章寫到這里似乎也差不多了,但我還想糾正一點(diǎn),網(wǎng)上有很多對(duì)于三級(jí)緩存的描述如下:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
	...
	// 從上至下 分表代表這“三級(jí)緩存”
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //一級(jí)緩存
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二級(jí)緩存
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三級(jí)緩存
	...
	
	/** Names of beans that are currently in creation. */
	// 這個(gè)緩存也十分重要:它表示bean創(chuàng)建過程中都會(huì)在里面呆著~
	// 它在Bean開始創(chuàng)建時(shí)放值,創(chuàng)建完成時(shí)會(huì)將其移出~
	private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

	/** Names of beans that have already been created at least once. */
	// 當(dāng)這個(gè)Bean被創(chuàng)建完成后,會(huì)標(biāo)記為這個(gè) 注意:這里是set集合 不會(huì)重復(fù)
	// 至少被創(chuàng)建了一次的  都會(huì)放進(jìn)這里~~~~
	private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
}

但源碼中的順序卻不是如此(我使用的是 5.1.5 版本):

	/** Cache of singleton objects: bean name to bean instance. */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** Cache of singleton factories: bean name to ObjectFactory. */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/** Cache of early singleton objects: bean name to bean instance. */
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

但我想三級(jí)緩存的分層并不是依賴上面的源碼順序來分為一二三的,而應(yīng)該是根據(jù)從緩存中獲取對(duì)象的順序來分層的:

	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

最后,我結(jié)合上述的場(chǎng)景來分析下這三個(gè)“緩存”中元素的變化(省略了其它無關(guān)流程):

緩存變化

紅色線條代表取出元素,虛線代表最終將不存在。

這里做下總結(jié):

二級(jí)緩存也是能解決循環(huán)依賴的,使用三級(jí)緩存是為了幫助檢測(cè)提前暴露的對(duì)象在后期被修改的這種情況;

通過 earlySingletonObjects 持有被暴露的對(duì)象,然后在最終返回對(duì)象時(shí)進(jìn)行比對(duì)。如果不是同一個(gè)對(duì)象,則代表發(fā)生了對(duì)象后期被修改的情況。

到此這篇關(guān)于淺析Spring中的循環(huán)依賴問題的文章就介紹到這了,更多相關(guān)Spring的循環(huán)依賴內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring注解之@Conditional使用解析

    Spring注解之@Conditional使用解析

    這篇文章主要介紹了Spring注解之@Conditional使用解析,@Conditional注解可以說是SpringBoot的條件注解,表示組件只有在所有指定條件都匹配時(shí)才有資格注冊(cè),條件是可以在 bean 定義注冊(cè)之前??以編程方式確定的任何狀態(tài),需要的朋友可以參考下
    2024-01-01
  • 詳解Springboot配置文件的使用

    詳解Springboot配置文件的使用

    在springboot項(xiàng)目中,也可以使用yml類型的配置文件代替properties文件。接下來通過本文給大家分享Springboot配置文件的使用,感興趣的朋友一起看看吧
    2017-07-07
  • IDEA啟動(dòng)Tomcat時(shí)控制臺(tái)出現(xiàn)亂碼問題及解決

    IDEA啟動(dòng)Tomcat時(shí)控制臺(tái)出現(xiàn)亂碼問題及解決

    這篇文章主要介紹了IDEA啟動(dòng)Tomcat時(shí)控制臺(tái)出現(xiàn)亂碼問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-02-02
  • SpringBoot接入支付寶支付的方法步驟

    SpringBoot接入支付寶支付的方法步驟

    這篇文章主要介紹了SpringBoot接入支付寶支付的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • Java設(shè)計(jì)模式之裝飾模式詳解

    Java設(shè)計(jì)模式之裝飾模式詳解

    這篇文章主要介紹了Java設(shè)計(jì)模式中的裝飾者模式,裝飾者模式即Decorator Pattern,裝飾模式是在不必改變?cè)愇募褪褂美^承的情況下,動(dòng)態(tài)地?cái)U(kuò)展一個(gè)對(duì)象的功能,裝飾模式又名包裝模式。裝飾器模式以對(duì)客戶端透明的方式拓展對(duì)象的功能,是繼承關(guān)系的一種替代方案
    2022-08-08
  • JSON Web Token(JWT)原理入門教程詳解

    JSON Web Token(JWT)原理入門教程詳解

    這篇文章主要為大家介紹了JSON Web Token(JWT)原理入門教程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04
  • springboot獲取微信JSDK簽名信息的實(shí)現(xiàn)示例

    springboot獲取微信JSDK簽名信息的實(shí)現(xiàn)示例

    本文介紹了如何在Spring Boot應(yīng)用中獲取微信JSDK的簽名信息,包括獲取接口URL、參數(shù)設(shè)置、簽名算法和獲取簽名結(jié)果的步驟,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-11-11
  • 使用HttpClient調(diào)用接口的實(shí)例講解

    使用HttpClient調(diào)用接口的實(shí)例講解

    下面小編就為大家?guī)硪黄褂肏ttpClient調(diào)用接口的實(shí)例講解。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-10-10
  • 深入分析Comparable與Comparator及Clonable三個(gè)Java接口

    深入分析Comparable與Comparator及Clonable三個(gè)Java接口

    接口不是類,而是對(duì)類的一組需求描述,這些類要遵從接口描述的統(tǒng)一格式進(jìn)行定義,這篇文章主要為大家詳細(xì)介紹了Java的Comparable,Comparator和Cloneable的接口,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-05-05
  • Java中Minio的基本使用詳解

    Java中Minio的基本使用詳解

    這篇文章主要介紹了Java中Minio的基本使用詳解,MinIO 是一個(gè)基于Apache License v2.0開源協(xié)議的對(duì)象存儲(chǔ)服務(wù),它兼容亞馬遜S3云存儲(chǔ)服務(wù)接口,非常適合于存儲(chǔ)大容量非結(jié)構(gòu)化的數(shù)據(jù),例如圖片、視頻、日志文件、備份數(shù)據(jù)和容器/虛擬機(jī)鏡像等,需要的朋友可以參考下
    2024-01-01

最新評(píng)論