最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

關于SpringBoot的三級緩存的思考問題小結

 更新時間:2025年10月09日 14:50:47   作者:GEMjay  
文章解析SpringBoot三級緩存機制,指出其核心在于通過提前暴露早期引用解決單例Bean屬性注入的循環(huán)依賴,支持AOP代理,但構造函數注入無法解決,本質是為特殊場景提供生命周期彈性通道,本文給大家介紹對于SpringBoot的三層緩存的思考,感興趣的朋友一起看看吧

前言

在閱讀 Spring Boot 源碼的過程中,我對 createBean 方法中那段廣為人知的邏輯——“三級緩存解決循環(huán)依賴”——產生了濃厚的興趣。我花了相當多的時間去研究源碼,并參考了許多網上的解析與討論。

關于為什么要設計三級緩存,不同的說法層出不窮,網上的博客質量參差不齊:有人認為這是出于性能優(yōu)化的考慮,有人認為是為了支持 AOP 代理的提前暴露,也有人認為兩級緩存無法完全應對循環(huán)依賴的問題。起初我也在這些觀點之間反復權衡,但隨著理解的深入,我找到了自己的答案。

我認為,Spring Boot 之所以采用三級緩存的根本目的,并不在于性能或特定功能的支持,而在于在遵循 Bean 生命周期語義的前提下,允許在循環(huán)依賴的特殊場景中適度突破這一語義。

換句話說,三級緩存主要維護了 Spring 對 Bean 創(chuàng)建過程的規(guī)范性。

一、SpringBoot具體解決了什么循環(huán)依賴?

Spring Boot中,Bean 之間的依賴可以通過多種方式注入,例如:

  • 構造函數注入(Constructor Injection)
  • 字段注入(@Autowired)
  • Setter 方法注入
  • 接口回調注入(如 BeanFactoryAware、ApplicationContextAware 等)

但并不是所有注入方式都可能導致循環(huán)依賴,也不是所有循環(huán)依賴 Spring 都能“救回來”。三級緩存機制所能解決的,實際上只是**“單例 Bean 之間通過屬性注入(字段或 Setter)產生的循環(huán)依賴”**。

這里的構造函數注入最為特殊,因為它的注入時機是最早的,所以這里我將它們分為構造函數注入非構造函數注入

1.1 非構造函數注入

@Component
@Data
public class CycleDependenceTestA {
    @Autowired
    public CycleDependenceTestB cycleDependenceTestB;
    public CycleDependenceTestA() {}
}
@Component
@Data
public class CycleDependenceTestB {
    @Autowired
    public CycleDependenceTestA cycleDependenceTestA;
    public CycleDependenceTestB() {}
}

容器正常啟動,循環(huán)依賴問題被解決。

1.2 構造函數注入

構造函數注入比較特殊,因為它的注入時機是最早的。

@Component
@Data
public class CycleDependenceTestA {
    public CycleDependenceTestB cycleDependenceTestB;
    public CycleDependenceTestA(CycleDependenceTestB cycleDependenceTestB) {
        this.cycleDependenceTestB = cycleDependenceTestB;
    }
}
@Component
@Data
public class CycleDependenceTestB {
    public CycleDependenceTestA cycleDependenceTestA;
    public CycleDependenceTestB(CycleDependenceTestA cycleDependenceTestA) {
        this.cycleDependenceTestA = cycleDependenceTestA;
    }
}

結果會報錯:

Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
|  cycleDependenceTestA defined in file [E:\Java space\codes\SpringBootDemo\target\classes\org\example\springbootdemo\beans\CycleDependenceTestA.class]
↑     ↓
|  cycleDependenceTestB defined in file [E:\Java space\codes\SpringBootDemo\target\classes\org\example\springbootdemo\beans\CycleDependenceTestB.class]
└─────┘
Action:
Despite circular references being allowed, the dependency cycle between beans could not be broken. Update your application to remove the dependency cycle.
Process finished with exit code 1

SpringBoot無法解決這種情況,原因很簡單,往后看了解三層緩存原理后,自然會明白。

1.3 構造函數和非構造函數混合注入

這里的情況最為特殊和有趣,大家可以猜一猜,這種情況下的循環(huán)依賴的問題能否解決呢?
我在這里告訴大家答案:50%概率可以解決,和注入的先后順序有關系。

1.3.1 不能解決的案例

@Component
@Data
public class CycleDependenceTestA {
    public CycleDependenceTestB cycleDependenceTestB;
    public CycleDependenceTestA(CycleDependenceTestB cycleDependenceTestB) {
        this.cycleDependenceTestB = cycleDependenceTestB;
    }
}
@Component
@Data
public class CycleDependenceTestB {
    @Autowired
    public CycleDependenceTestA cycleDependenceTestA;
    public CycleDependenceTestB() {}
}

結果是報了循環(huán)依賴的錯誤的:

Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
|  cycleDependenceTestA defined in file [E:\Java space\codes\SpringBootDemo\target\classes\org\example\springbootdemo\beans\CycleDependenceTestA.class]
↑     ↓
|  cycleDependenceTestB (field public org.example.springbootdemo.beans.CycleDependenceTestA org.example.springbootdemo.beans.CycleDependenceTestB.cycleDependenceTestA)
└─────┘
Action:
Despite circular references being allowed, the dependency cycle between beans could not be broken. Update your application to remove the dependency cycle.
Process finished with exit code 1

1.3.2 可以解決的案例

@Component
@Data
public class CycleDependenceTestA {
    @Autowired
    public CycleDependenceTestB cycleDependenceTestB;
    public CycleDependenceTestA() {}
}
@Component
@Data
public class CycleDependenceTestB {
    public CycleDependenceTestA cycleDependenceTestA;
    public CycleDependenceTestB(CycleDependenceTestA cycleDependenceTestA) {
        this.cycleDependenceTestA = cycleDependenceTestA;
    }
}

容器正常啟動

1.3.3 總結

上述兩個案例中,一個循環(huán)依賴被解決,另一個無法被解決,代碼區(qū)別是什么?其實就是執(zhí)行順序的問題。至于為什么,先賣個關子,如果你看過底層源碼,了解Bean的生命周期,創(chuàng)建流程,自然會理解。請往下看。

二、如何解決的?

2.1 依賴注入時機

核心代碼在AbstractAutowireCapableBeanFactory的doCreateBean方法中。

// 核心邏輯:AbstractAutowireCapableBeanFactory#doCreateBean
// 1. 創(chuàng)建 Bean 實例(構造函數注入在此階段完成)
instanceWrapper = createBeanInstance(beanName, mbd, args);
// 2. 暴露早期引用,將用于生成代理的 ObjectFactory 放入第三級緩存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
// 3. 屬性依賴注入(此階段執(zhí)行 @Autowired、@Value 等注解邏輯)
//   關鍵處理器:AutowiredAnnotationBeanPostProcessor
populateBean(beanName, mbd, instanceWrapper);
// 4. Bean 初始化(執(zhí)行初始化回調與 AOP 代理創(chuàng)建等邏輯)
//   關鍵處理器:AbstractAutoProxyCreator 及其他 BeanPostProcessor
exposedObject = initializeBean(beanName, exposedObject, mbd);

2.2 三層緩存

Spring 在解決循環(huán)依賴問題時,核心邏輯位于 DefaultSingletonBeanRegistry 類中。

2.2.1 三個重要的緩存容器

它們共同構成了所謂的三級緩存機制:

// 一級緩存:存放完全初始化完成的單例 Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二級緩存:存放提前暴露但尚未完全初始化的 Bean 實例
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// 三級緩存:存放可以生成 Bean 早期引用的工廠(通常是用于生成代理的 ObjectFactory)
private final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>(16);

2.2.2 核心方法:getSingleton()

// 源碼
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) {
				if (!this.singletonLock.tryLock()) {
					// Avoid early singleton inference outside of original creation thread.
					return null;
				}
				try {
					// 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();
								// Singleton could have been added or removed in the meantime.
								if (this.singletonFactories.remove(beanName) != null) {
									this.earlySingletonObjects.put(beanName, singletonObject);
								}
								else {
									singletonObject = this.singletonObjects.get(beanName);
								}
							}
						}
					}
				}
				finally {
					this.singletonLock.unlock();
				}
			}
		}
		return singletonObject;
	}

核心邏輯如下:

  1. 先從一級緩存中找
    Object singletonObject = this.singletonObjects.get(beanName);
  2. 一級緩存找不到,再從二級緩存找
    singletonObject = this.earlySingletonObjects.get(beanName);
    
  3. 二級緩存也沒有,則從三級緩存取出工廠并生成早期引用
    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
    if (singletonFactory != null) {
        singletonObject = singletonFactory.getObject();
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.put(beanName, singletonObject);
    }
    

2.3 說明

上面的代碼展示了 Spring 解決循環(huán)依賴的核心實現邏輯。為了更清晰地理解,我們可以先回到 Bean 的創(chuàng)建生命周期。

一個 Bean 從創(chuàng)建到最終可用,大致會經歷三個階段:
實例化 → 屬性注入 → 初始化。
只有當 Bean 完成初始化后,才能被認為是一個“完整可用”的 Bean。

循環(huán)依賴問題的關鍵在于:
當 Bean A 依賴 Bean B,而 Bean B 又依賴 Bean A 時,如果嚴格按照生命周期順序執(zhí)行,那么雙方都會在“屬性注入階段”卡住——因為此時彼此都還沒有完成創(chuàng)建。

Spring 的解決思路是:

在實例化完成但尚未初始化之前,提前暴露一個可以引用的 Bean 對象,供其他 Bean 使用。

換句話說,即使一個 Bean 還沒完全準備好(屬性未注入、后置處理器未執(zhí)行),Spring 也允許通過三級緩存機制,將它的“早期引用”暴露出去。這樣,另一個 Bean 在注入時就能拿到一個有效的引用,從而打破循環(huán)依賴的僵局。

三、三級緩存到底解決了什么問題

3.1 第三級緩存的特殊性

private final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>(16);

第三級緩存相較于前兩級緩存更為特殊——它保存的不是 Bean 實例本身,而是一個 ObjectFactory 對象,也就是一個可執(zhí)行的回調函數。
要理解這種設計的意義,我們需要看看 Spring 在向第三級緩存注冊時,究竟放入了什么邏輯:

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
// class 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;
}

Spring 會遍歷所有實現了 SmartInstantiationAwareBeanPostProcessor 接口的后置處理器,讓它們有機會提前介入 Bean 的引用創(chuàng)建過程。

其中最典型的后置處理器就是 AbstractAutoProxyCreator,它正是 Spring AOP 的底層核心之一。

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

這里的 wrapIfNecessary() 方法會判斷當前 Bean 是否需要被 AOP 切面增強:

如果需要增強,就在此時創(chuàng)建代理對象并返回;

如果不需要,則直接返回原始對象。

換句話說,這一步可能會生成 Bean 的代理對象,并將其作為“早期引用”暴露出去。

第三級緩存的存在意義就在于:當出現循環(huán)依賴時,如果另一個 Bean 需要當前 Bean 的引用,Spring 能通過第三級緩存中的 ObjectFactory 提前觸發(fā)代理邏輯,返回正確的引用(包括可能的代理對象),從而保證依賴注入和最終 Bean 一致性。

3.2 二級緩存能不能解決循環(huán)依賴問題

事實上,從循環(huán)依賴本身的角度來看,二級緩存也完全可以解決問題。
因為三級緩存的核心功能,是在 Bean 初始化之前允許返回一個“早期引用”。
如果我們直接在實例化之后、屬性注入之前,將早期引用放入二級緩存,同樣能夠實現循環(huán)依賴的解環(huán)。

假設我們對 Spring 的邏輯稍作改造,不使用三級緩存,而是直接在實例化后生成早期引用并放入二級緩存:

// AbstractAutowireCapableBeanFactory(偽代碼改造)
if (earlySingletonExposure) {
	if (logger.isTraceEnabled()) {
		logger.trace("Eagerly caching bean '" + beanName +
				"' to allow for resolving potential circular references");
	}
	addEarlySingletonObjects(beanName, getEarlyBeanReference(beanName, mbd, bean));
}

在這段偽代碼中,Spring 不再保存 ObjectFactory,而是直接調用
getEarlyBeanReference() 獲取早期引用(包括可能的 AOP 代理對象),
然后立即將其放入二級緩存 earlySingletonObjects 中,供其他 Bean 在依賴注入時使用。

從表面上看,這樣確實可以達到同樣的效果:

循環(huán)依賴照樣被解決;

AOP 代理也能提前生成;

性能上沒有任何差別(甚至更直接)。

3.3 三級緩存實際解決的問題

Spring 通過三級緩存設計了一個延遲生成早期引用的機制:它既能解決循環(huán)依賴,又能在大多數情況下保持 Bean 生命周期和 AOP 代理邏輯的語義一致性。

具體來說:

在 正常創(chuàng)建流程 中,Bean 會嚴格遵循生命周期:實例化 → 屬性注入 → 初始化 → 后置處理器(生成 AOP 代理)。

三級緩存的作用是為 循環(huán)依賴 這種突發(fā)情況 提供一個彈性通道:當 Bean 之間存在循環(huán)依賴時,Spring 可以通過三級緩存提前生成早期引用(可能是代理對象),從而打破循環(huán)依賴的僵局。

換句話說,三級緩存是一種 “在必要時允許突破生命周期規(guī)范的機制”,保證循環(huán)依賴能夠被安全解決,同時不會影響絕大多數 Bean 的正常創(chuàng)建流程。

到此這篇關于對于SpringBoot的三層緩存的思考的文章就介紹到這了,更多相關SpringBoot三層緩存內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java實現TXT轉Excel并讀取Excel內容到List集合的完整代碼

    Java實現TXT轉Excel并讀取Excel內容到List集合的完整代碼

    在Java開發(fā)中,我們經常會遇到文件格式轉換的需求,比如將txt???文件轉換為??excel???文件,并且可能需要進一步處理??excel文件中的數據,本文將詳細介紹如何使用 Java 代碼完成這個任務,我們會使用???EasyExcel????庫來實現???excel???文件的讀寫操作
    2025-07-07
  • Java CAS基本實現原理代碼實例解析

    Java CAS基本實現原理代碼實例解析

    這篇文章主要介紹了Java CAS基本實現原理代碼實例解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-07-07
  • Java工具類實現高效編寫報表

    Java工具類實現高效編寫報表

    對于報表數據大部分情況下使用寫sql的方式為大屏/報表提供數據來源,但是對于某些復雜情況下僅僅使用sql無法實現,這篇文章主要介紹了Java工具類實現高效編寫報表
    2022-11-11
  • springboot使JUL實現日志管理功能

    springboot使JUL實現日志管理功能

    這篇文章主要介紹了springboot使JUL實現日志管理功能,本文分步驟給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • SpringMvc自動裝箱及GET請求參數原理解析

    SpringMvc自動裝箱及GET請求參數原理解析

    這篇文章主要介紹了SpringMvc自動裝箱及GET請求參數原理解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-09-09
  • 一文全解Java 泛型

    一文全解Java 泛型

    文章主要介紹了Java泛型的概念、定義、使用方法及其優(yōu)勢,文章詳細介紹了泛型類、泛型方法、泛型接口以及通配符的使用方法和應用場景,最后強調了類型擦除的概念和通配符的使用注意事項,感興趣的朋友跟隨小編一起看看吧
    2026-05-05
  • Spring框架基于注解開發(fā)CRUD詳解

    Spring框架基于注解開發(fā)CRUD詳解

    這篇文章主要為大家詳細介紹了Spring框架基于注解開發(fā)CRUD,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • SpringBoot異步處理全過程

    SpringBoot異步處理全過程

    這篇文章主要介紹了SpringBoot異步處理全過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-06-06
  • Java重入鎖(ReentrantLock)從入門到源碼深度解析

    Java重入鎖(ReentrantLock)從入門到源碼深度解析

    本文給大家介紹Java重入鎖(ReentrantLock)從入門到源碼深度解析,通過本文學習可以全方位地認識ReentrantLock,從基本概念到高級特性,從使用方式到源碼剖析,從底層原理到實際應用,感興趣的朋友跟隨小編一起看看吧
    2026-02-02
  • Java中ArrayList的常見用法示例小結

    Java中ArrayList的常見用法示例小結

    本文介紹了Java的ArrayList,它是一個動態(tài)數組,可以自動調整大小,支持添加、刪除、獲取元素等操作,同時,還討論了如何存儲基本數據類型以及在多線程環(huán)境下的使用注意事項,感興趣的朋友一起看看吧
    2025-02-02

最新評論

淮阳县| 南开区| 嘉祥县| 鸡泽县| 微山县| 拜城县| 望奎县| 辉南县| 漳州市| 蓝田县| 滁州市| 洱源县| 沅江市| 洛浦县| 石狮市| 综艺| 大洼县| 广水市| 宜川县| 丹凤县| 晋宁县| 兴城市| 南召县| 扬中市| 兴隆县| 泾源县| 宜黄县| 东兴市| 汶上县| 阿荣旗| 六安市| 揭东县| 信阳市| 景泰县| 盈江县| 分宜县| 图片| 孙吴县| 渭南市| 莱西市| 墨竹工卡县|