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

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

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

文章閱讀前推薦

推薦先去看看源碼,源碼很短,但是對于我們在腦子里構(gòu)建一個(gè)完整思路很重要??雌饋矸浅:唵?,只需要雙擊shift,全局查找文件:AbstractAutowireCapableBeanFactory,找到550行左右的doCreateBean方法,重點(diǎn)看一下580行到600行這20行代碼就行,包含了三級緩存、屬性注入、初始化,精華都在這20行,實(shí)在沒條件的可以直接看文末附帶的doCreateBean方法源碼

Spring可以自動(dòng)解決的循環(huán)依賴

public class AService {
	@Autowired
    private BService bService;
}

public class BService {
	@Autowired
    private AService aService;
}

Spring無法自動(dòng)解決構(gòu)造器的循環(huán)依賴

public class DService {
    public DService(CService cService) {
    	...
    }
}

public class CService {
    public CService(DService dService) {
    	...
    }
}

源碼中關(guān)于三級緩存的定義

// 一級緩存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二級緩存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// 三級緩存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

所以說,其實(shí)三級緩存就是三個(gè)Map而已

三級緩存有什么區(qū)別?

一級緩存

一級緩存里存的是成品對象,實(shí)例化和初始化都完成了,我們的應(yīng)用中使用的對象就是一級緩存中的

二級緩存

二級緩存中存的是半成品,沒有完成屬性注入和初始化,用來解決對象創(chuàng)建過程中的循環(huán)依賴問題
早期暴露出去的Bean,其實(shí)也就是解決循環(huán)依賴的Bean。早期的意思就是沒有完完全全創(chuàng)建好,但是由于有循環(huán)依賴,就需要把這種Bean提前暴露出去。其實(shí) 早期暴露出去的Bean 跟 完完全全創(chuàng)建好的Bean 他們是同一個(gè)對象,只不過早期Bean里面的注解可能還沒處理,完完全全的Bean已經(jīng)處理了完了,但是他們指的還是同一個(gè)對象,只不過它們是在Bean創(chuàng)建過程中處于的不同狀態(tài)

三級緩存

三級緩存中存的是 ObjectFactory<?> 類型的代理工廠對象,用于處理存在 AOP 時(shí)的循環(huán)依賴問題
存的是每個(gè)Bean對應(yīng)的ObjectFactory對象,通過調(diào)用這個(gè)對象的getObject方法,就可以獲取到早期暴露出去的Bean。
注意:這里有個(gè)很重要的細(xì)節(jié)就是三級緩存只會對單例的Bean生效,像多例的是無法利用到三級緩存的,通過三級緩存所在的類名DefaultSingletonBeanRegistry就可以看出,僅僅是對SingletonBean也就是單例Bean有效果。

發(fā)生循環(huán)依賴時(shí)的執(zhí)行流程(精華必讀)

正常不存在循環(huán)依賴的A、B對象是依次創(chuàng)建的,但是如果存在循環(huán)依賴的話,創(chuàng)建A的過程中,會順便把B也創(chuàng)建了。注意,每次獲取bean對象都會先去一級緩存看有沒有值。
具體流程是:
1、遍歷待創(chuàng)建的所有beanName,第一次遍歷,開始獲取A,此時(shí)緩存中沒有,會開始正常創(chuàng)建流程
2、A初始創(chuàng)建完成,然后判斷A是否是單例,且沒有創(chuàng)建完畢,如果是,那么就會把A的beanFactory存入三級緩存
3、A開始處理@Autowired注解,開始注入B屬性,于是嘗試從緩存獲取B,獲取不到,則開始正常創(chuàng)建B的流程
4、B初始創(chuàng)建完成,同樣判斷B是否是單例,且沒有創(chuàng)建完畢,如果是,那么就會把B的beanFactory存入三級緩存
5、B開始處理@Autowired注解,開始注入A屬性,于是依次從一級緩存、二級緩存查找A屬性,都沒有就嘗試從三級緩存獲取A的beanFactory,通過beanFactory.getObject()方法獲取A屬性,接下來把A存入二級緩存,清除三級緩存。因?yàn)榇藭r(shí)能獲取到A,所以B的A屬性能填充成功,B接著執(zhí)行初始化,B處于實(shí)例化、初始化都完成的完全狀態(tài)
6、B執(zhí)行addSington(),把完全狀態(tài)的B存入一級緩存,清空二三級緩存(實(shí)際只有三級有值)
7、A繼續(xù)開始填充B屬性,于是調(diào)用beanFactory.getBean()獲取B,第六步已經(jīng)把B存入一級緩存,此時(shí)直接返回,填充成功,繼續(xù)執(zhí)行初始化,得到一個(gè)完全狀態(tài)的A
8、A執(zhí)行addSington(),把完全狀態(tài)的A存入一級緩存,清空二三級緩存(實(shí)際只有二級有值)
9、第二次遍歷,開始獲取B,此時(shí)一級緩存中有B,直接返回。
至此A、B全部實(shí)例化、初始化完成

疑惑解答

問題一:步驟5中,為什么要把B放入二級緩存?
答:主要是怕還有其他的循環(huán)依賴,如果還有的話,直接從二級緩存中就能拿到早期的AService對象

問題二:步驟6中,為什么要清空二三級緩存?
答:因?yàn)楹罄m(xù)其他bean中也需要注入B時(shí),會按順序從一級緩存直到三級緩存查找,一級緩存有了,二三級緩存中的就不需要了,節(jié)省空間

問題三:文章開頭提到,構(gòu)造器造成的循環(huán)依賴三級緩存解決不了,為什么?
答:因?yàn)闃?gòu)造器循環(huán)依賴是發(fā)生在bean實(shí)例化階段,此時(shí)連早期對象都還沒創(chuàng)建出來,拿什么放到三級緩存。三級緩存只能是在bean實(shí)例化之后,才能起到作用

問題四:不用三級緩存,只用二級緩存能不能解決循環(huán)依賴?
答:不能,因?yàn)橥ㄟ^ObjectFactory獲取的Bean可能是兩種類型,第一種就是實(shí)例化階段創(chuàng)建出來的對象,還是一種就是實(shí)例化階段創(chuàng)建出來的對象的代理對象。至于是不是代理對象,取決于你的配置,如果添加了事務(wù)注解又或是自定義AOP切面,那就需要代理。假設(shè)舍棄第三級緩存,也就是沒有ObjectFactory,那么就需要往第二緩存放入早期的Bean,那么是放沒有代理的Bean還是被代理的Bean呢,這是在后面的屬性注入階段,處理注解的時(shí)候才能分辨的?
1)如果直接往二級緩存添加沒有被代理的Bean,那么可能注入給其它對象的Bean跟最后最后完全生成的Bean是不一樣的,因?yàn)樽詈笊傻氖谴韺ο?,這肯定是不允許的;
2)那么如果直接往二級緩存添加一個(gè)代理Bean呢?
● 假設(shè)沒有循環(huán)依賴,提前暴露了代理對象,那么如果跟最后創(chuàng)建好的不一樣,那么項(xiàng)目啟動(dòng)就會報(bào)錯(cuò),
● 假設(shè)沒有循環(huán)依賴,使用了ObjectFactory,那么就不會提前暴露了代理對象,到最后生成的對象是什么就是什么,就不會報(bào)錯(cuò),
● 如果有循環(huán)依賴,不論怎樣都會提前暴露代理對象,那么如果跟最后創(chuàng)建好的不一樣,那么項(xiàng)目啟動(dòng)就會報(bào)錯(cuò)
通過上面分析,如果沒有循環(huán)依賴,使用ObjectFactory,就減少了提前暴露代理對象的可能性,從而減少報(bào)錯(cuò)的可能。

問題五:如果把二級緩存去掉,只留下一級、三級緩存呢?
答:假設(shè)舍棄第二級緩存,也就是沒有存放早期的Bean的緩存,其實(shí)肯定也不行。上面說過,ObjectFactory其實(shí)獲取的對象可能是代理的對象,那么如果每次都通過ObjectFactory獲取代理對象,那么每次都重新創(chuàng)建一個(gè)代理對象,這肯定也是不允許的。

doCreateBean()方法源碼(帶注釋)

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		//1、通過BeanDefinition實(shí)例化對象
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		final Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		// Allow post-processors to modify the merged bean definition.
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		//對象是否單例、是否未創(chuàng)建完成
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			//將對象的工廠加入到三級緩存
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
			//屬性注入(在這里解析@Autowired注解時(shí),觸發(fā)循環(huán)依賴)
			populateBean(beanName, mbd, instanceWrapper);
			//初始化
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

		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);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					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;
	}



從緩存中獲取Bean的源碼

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 從一級緩存中獲取
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 如果一級緩存里沒有 且 bean正在創(chuàng)建中
        synchronized (this.singletonObjects) {
            // 從二級緩存里獲取
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 二級緩存沒有 從三級緩存獲取一個(gè)工廠
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // 能獲取到工廠 則創(chuàng)建bean
                    singletonObject = singletonFactory.getObject();
                    // 把實(shí)例存入二級緩存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    // 把工廠從三級緩存移除
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}




–我是“三七有脾氣”,一個(gè)在互聯(lián)網(wǎng)"茍且偷生"的Java程序員
“如果感覺博客對你有用,麻煩給個(gè)點(diǎn)贊、評論、收藏,謝謝文章來源地址http://www.zghlxwxcb.cn/news/detail-843290.html

到了這里,關(guān)于Spring使用三級緩存解決循環(huán)依賴?終于完全弄明白了的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

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

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • spring 的循環(huán)依賴以及spring為什么要用三級緩存解決循環(huán)依賴

    spring 的循環(huán)依賴以及spring為什么要用三級緩存解決循環(huán)依賴

    ??????? bean的生命周期 ??????? 這里簡單過一下 class -無參構(gòu)造 -普通對象 -依賴注入(對加了autowire等的屬性賦值) -初始化前-初始化 -初始化后(aop) -放入單例池的map(一級緩存) -bean對象 這里提一點(diǎn)單例bean單例bean 其實(shí)就是用mapbeanName,Bean對象創(chuàng)建的,多例bean就不

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

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

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

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

    解決有代理對象的循環(huán)依賴不一定要三級緩存,用二級甚至一級也能解決,下面討論下Spring為什么選擇三級緩存這個(gè)方案。 Spring最開始是沒有三級緩存的,后面版本因?yàn)橐肓薃OP,有了代理對象,又因?yàn)榇嬖谘h(huán)依賴,為了保證依賴注入過程注入的是代理對象,且不完全打破

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

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

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

    2024年02月11日
    瀏覽(21)
  • springIoc依賴注入循環(huán)依賴三級緩存

    springIoc依賴注入循環(huán)依賴三級緩存

    理論思想,原來的對象是由使用者來進(jìn)行控制,有了spring之后,可以把整個(gè)對象交給spring來幫我們進(jìn)行管理 依賴注入,把對應(yīng)的屬性的值注入到具體的對象中,@autowired,populateBean完成屬性的注入 beanFactory,存儲對象,使用map結(jié)構(gòu)來存儲,在spring中一般存在三級緩存,singleton

    2024年01月16日
    瀏覽(20)
  • Spring解決循環(huán)依賴

    Spring解決循環(huán)依賴

    目錄 什么是spring循環(huán)依賴 什么情況下循環(huán)依賴可以被處理? spring?如何解決循環(huán)依賴 創(chuàng)建A這個(gè)Bean的流程 答疑 疑問:在給B注入的時(shí)候?yàn)槭裁匆⑷胍粋€(gè)代理對象? 初始化的時(shí)候是對A對象本身進(jìn)行初始化,而容器中以及注入到B中的都是代理對象,這樣不會有問題嗎? 三級

    2024年02月22日
    瀏覽(24)
  • Spring解決循環(huán)依賴問題

    Spring解決循環(huán)依賴問題

    例如,就是A對象依賴了B對象,B對象依賴了A對象。(下面的代碼屬于 屬性的循環(huán)依賴 ,也就是初始化階段的循環(huán)依賴,區(qū)別與底下 構(gòu)造器的循環(huán)依賴 ) 問題來了: A Bean創(chuàng)建 —— 依賴了 B 屬性 ——? 觸發(fā) B Bean創(chuàng)建 ——? B 依賴了 A 屬性 ——? 需要 A Bean(但A Bean還在創(chuàng)建

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

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

    2024年02月06日
    瀏覽(17)
  • Spring怎么解決循環(huán)依賴問題?

    Spring怎么解決循環(huán)依賴問題?

    循環(huán)依賴是指 一個(gè)或多個(gè)對象之間存在直接或間接的依賴關(guān)系,這種依賴關(guān)系構(gòu)成一個(gè)環(huán)形調(diào)用 , 舉個(gè)例子 : A 依賴B , B依賴C , C依賴A , 這樣就形成了循環(huán)依賴; ? ①構(gòu)造器的循環(huán)依賴:這種依賴spring是處理不了的,直接拋出BeanCurrentlyInCreationException異常。 ②單例模式下的se

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

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

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

    2024年02月03日
    瀏覽(19)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

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

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包