欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Springboot詳細講解循環(huán)依賴

 更新時間:2022年06月16日 10:22:02   作者:滄鷲小hai  
最近在使用Springboot做項目的時候,遇到了一個循環(huán)依賴的問題,所以下面這篇文章主要給大家介紹了關(guān)于springboot循環(huán)依賴實現(xiàn)以及分析的相關(guān)資料,需要的朋友可以參考下

一、循環(huán)依賴

顧名思義多個類中的依賴形成了環(huán)路,形成了類似于死鎖的情況,導(dǎo)致springboot在啟動時無法為我們創(chuàng)建Bean。通俗來說 就是beanA中依賴了beanB,beanB中也依賴了beanA。

spring是支持循環(huán)依賴的,但是默認只支持單例的循環(huán)依賴,如果bean中依賴了原型bean,則需要加上lookup方法。Spring會為我們解決循環(huán)依賴。

@Autowired是通過三級緩存來解決循環(huán)依賴的。

@Autowired進行屬性注入可以解決循環(huán)依賴。原理是: Spring控制了bean的生命周期,先實例化bean,后注入bean的屬性。 Spring中記錄了正在創(chuàng)建中的bean(已經(jīng)實例化但還沒初始化完畢的bean),所以可以再注入屬性是,從記錄的ben中去依賴的對象。

相對而言,單純使用構(gòu)造器注入就無法解決循環(huán)依賴。因為在構(gòu)造時就需要傳入依賴的對象,導(dǎo)致無法實例化。但是 構(gòu)造器注入可以使用@Lazy解決循環(huán)依賴,在實例化時,傳入代理對象,真正使用時才會生成真正的對象。

二、循環(huán)依賴形成條件(使用構(gòu)造器注入)

1、使用構(gòu)造方法的方式來注入依賴,并且類A中依賴類B,類B也同時依賴類A,這樣兩個類都無法正常進行Bean的創(chuàng)建,就會拋出異常:BeanCurrentlyInCreationException

@Component
public class A {
    private B b;
    @Autowired
    public A(B b) {
        this.b = b;
    }
}
@Component
public class B {
    private A a;
    @Autowired
    public B(A a) {
        this.a = a;
    }
}

解決方法之一:可以使用lazy注解,延遲加載依賴

@Component
public class A {
    private B b;
    @Autowired
    @Lazy
    public A(B b) {
        this.b = b;
    }
}
@Component
public class B {
    private A a;
    @Autowired
    @Lazy
    public B(A a) {
        this.a = a;
    }
}

三、循環(huán)依賴形成條件(@Aysnc注解的bean生成了對象的代理)

從日志中可以看到是 tPartnerOrgService 這個bean出現(xiàn)了循環(huán)依賴

我在tPartnerOrgService 中使用了@Aysnc注解 進行異步處理,而@Aysnc注解的bean生成了對象的代理,導(dǎo)致Spring bean最終加載的不是一個原始對象導(dǎo)致了此次問題的發(fā)生。

解決方案1:給 tPartnerOrgService 加上@Lazy注解

解決方案2:代碼優(yōu)化,不要讓@Async的Bean參與循環(huán)依賴

四、針對以上問題對Spring如何解決循環(huán)依賴進行詳細闡述

首先Spring維護了三個Map,也就是我們通常說的三級緩存

  • singletonObjects:俗稱單例池,緩存創(chuàng)建完成的單例
  • BeansingletonFactories:映射創(chuàng)建Bean的原始工廠
  • earlySingletonObjects:映射Bean的早期引用,也就是說這個Map里的Bean不是完整的,只是完成了實例化,但還沒有初始化

Spring通過三級緩存解決了循環(huán)依賴,其中一級緩存為單例池(singletonObjects),二級緩存為早期曝光對象earlySingletonObjects,三級緩存為早期曝光對象工廠(singletonFactories)。

當(dāng)A、B兩個類發(fā)生循環(huán)引用時,在A完成實例化后,就使用實例化后的對象去創(chuàng)建一個對象工廠,并添加到三級緩存中,如果A被AOP代理,那么通過這個工廠獲取到的就是A代理后的對象,如果A沒有被AOP代理,那么這個工廠獲取到的就是A實例化的對象。

當(dāng)A進行屬性注入時,會去創(chuàng)建B,同時B又依賴了A,所以創(chuàng)建B的同時又會去調(diào)用getBean(a)來獲取需要的依賴,此時的getBean(a)會從緩存中獲取,第一步,先獲取到三級緩存中的工廠;第二步,調(diào)用對象工工廠的getObject方法來獲取到對應(yīng)的對象,得到這個對象后將其注入到B中。

緊接著B會走完它的生命周期流程,包括初始化、后置處理器等。當(dāng)B創(chuàng)建完后,會將B再注入到A中,此時A再完成它的整個生命周期。至此,循環(huán)依賴結(jié)束!

簡單一句話說:先去緩存里找Bean,沒有則實例化當(dāng)前的Bean放到Map,如果有需要依賴當(dāng)前Bean的,就能從Map取到。

針對上面的@Aysnc注解產(chǎn)生的循環(huán)依賴進行分析:

有@Aysnc注解的bean最后生成了一個代理對象,我們結(jié)合Spring bean創(chuàng)建的流程來分析這次問題。

  • beanA開始初始化,beanA實例化完成后給beanA的依賴屬性beanB進行賦值
  • beanB開始初始化,beanB實例化完成后給beanB的依賴屬性beanA進行賦值
  • 因為beanA是支持循環(huán)依賴的,所以可以在earlySingletonObjects中可以拿到beanA的早期引用的,但是因為beanB打了@Aysnc注解并不能在earlySingletonObjects中可以拿到早期引用
  • 接下來執(zhí)行執(zhí)行initializeBean(Object existingBean, String beanName)方法,這里beanA可以正常實例化完成,但是因為beanB打了@Aysnc注解,所以向Spring IOC容器中增加了一個代理對象,也就是說beanAbeanB并不是一個原始對象,而是一個代理對象
  • 接下來進行執(zhí)行doCreateBean方法時對進行檢測,以下代碼有所刪減,只保留核心邏輯代碼
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {
		if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					// 重點在這里,這里會遍歷所有依賴的bean,如果beanA依賴beanB和緩存中的beanB不相等
					// 也就是說beanA本來依賴的是一個原始對象beanB,但是這個時候發(fā)現(xiàn)beanB是一個代理對象,就會增加到actualDependentBeans
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					// 發(fā)現(xiàn)actualDependentBeans不為空,就發(fā)生了我們最開始截圖的錯誤
					if (!actualDependentBeans.isEmpty()) {
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}
		// Register bean as disposable.
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}
		return exposedObject;
	}

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

相關(guān)文章

  • Intellij IDEA 旗艦版創(chuàng)建 Spring MVC 項目踩過的坑

    Intellij IDEA 旗艦版創(chuàng)建 Spring MVC 項目踩過的坑

    IDEA旗艦版可以直接創(chuàng)建Spring MVC項目,但創(chuàng)建后的項目并不是直接就可以運行,還需要進行一些配置。這篇文章主要介紹了Intellij IDEA 旗艦版創(chuàng)建 Spring MVC 項目踩坑記 ,需要的朋友可以參考下
    2020-03-03
  • Java獲取請求頭信息的操作步驟

    Java獲取請求頭信息的操作步驟

    這篇文章主要介紹了Java獲取請求頭信息,本文分步驟結(jié)合實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-09-09
  • Java簡單驗證身份證功能示例

    Java簡單驗證身份證功能示例

    這篇文章主要介紹了Java簡單驗證身份證功能,涉及java針對字符串的截取、判斷相關(guān)操作技巧,需要的朋友可以參考下
    2017-06-06
  • 在springboot中使用AOP進行全局日志記錄

    在springboot中使用AOP進行全局日志記錄

    這篇文章主要介紹就在springboot中使用AOP進行全局日志記錄,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • JAVA中關(guān)于Long類型返回前端精度丟失問題處理辦法

    JAVA中關(guān)于Long類型返回前端精度丟失問題處理辦法

    這篇文章主要介紹了后端JavaBean的id屬性從Long類型改為雪花算法后出現(xiàn)的精度丟失問題,解決方案包括將id字段類型改為字符串或使用Jackson序列化方式,需要的朋友可以參考下
    2024-11-11
  • swagger如何返回map字段注釋

    swagger如何返回map字段注釋

    這篇文章主要介紹了swagger如何返回map字段注釋的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • java的json解析類庫使用示例

    java的json解析類庫使用示例

    這篇文章主要介紹了java的json解析類庫使用方法,這里使用Zson解析json,這是一個開源的json處理類庫
    2014-03-03
  • Spring?Boot開發(fā)時Java對象和Json對象之間的轉(zhuǎn)換

    Spring?Boot開發(fā)時Java對象和Json對象之間的轉(zhuǎn)換

    在Spring?Boot開發(fā)中,我們經(jīng)常需要處理Java對象和Json對象之間的轉(zhuǎn)換,本文將介紹如何在Spring?Boot項目中實現(xiàn)Java對象和Json對象之間的轉(zhuǎn)換,感興趣的朋友跟隨小編一起看看吧
    2023-09-09
  • Java 8 對 HashSet 元素進行排序的操作方法

    Java 8 對 HashSet 元素進行排序的操作方法

    Java 中HashSet是一個不保證元素順序的集合類,其內(nèi)部是基于 HashMap 實現(xiàn)的,HashSet不支持排序,我們在需要對HashSet 排序時,必須將其轉(zhuǎn)換為支持排序的集合或數(shù)據(jù)結(jié)構(gòu),如 List,本文將詳細介紹在 Java 8 中如何對 HashSet 中的元素進行排序,感興趣的朋友一起看看吧
    2024-11-11
  • Java實現(xiàn)PDF轉(zhuǎn)為Word文檔的示例代碼

    Java實現(xiàn)PDF轉(zhuǎn)為Word文檔的示例代碼

    眾所周知,PDF文檔除了具有較強穩(wěn)定性和兼容性外,?還具有較強的安全性,在工作中可以有效避免別人無意中對文檔內(nèi)容進行修改。本文將分為以下兩部分介紹如何在保持布局的情況下將PDF轉(zhuǎn)為Word文檔,希望對大家有所幫助
    2023-01-01

最新評論