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

Spring中的三級緩存使用及說明

 更新時間:2025年11月18日 09:41:36   作者:chen_179  
文章介紹了Spring框架中循環(huán)依賴的問題,包括循環(huán)依賴的定義、常見場景和解決方法,構(gòu)造器注入的循環(huán)依賴無法解決,而field屬性注入(setter方法注入)循環(huán)依賴可以解決,Spring通過“三級緩存”機(jī)制來解決循環(huán)依賴問題,其中singletonFactories是關(guān)鍵

常見問題

循環(huán)依賴

循環(huán)依賴:就是 N 個類循環(huán)(嵌套)使用。

簡單來說,就是多個 Bean 之間互相依賴或引用到對方,最終形成了 閉環(huán)。

循環(huán)依賴的關(guān)系可以表示成下圖:

上圖中的 A,B,C 代表三個不同的對象,上圖表示了三者的引用關(guān)系。

循環(huán)依賴不僅發(fā)生在多個類中,還可能發(fā)生在本類中,即 N = 1,這種事極限情況的循環(huán)依賴:自己依賴自己

注意:這里指的循環(huán)依賴不是方法之間的循環(huán)調(diào)用,而是對象的相互依賴關(guān)系。(因?yàn)榉椒ㄖg的循環(huán)調(diào)用有出口也能正常運(yùn)行)

代碼示例

public class Main {

    public static void main(String[] args) throws Exception {
        System.out.println(new A());
    }

}

class A {
    public A() {
        new B();
    }
}

class B {
    public B() {
        new A();
    }
}

運(yùn)行結(jié)果

Exception in thread "main" java.lang.StackOverflowError

從上面代碼可以看到,若構(gòu)造函數(shù)之間發(fā)生循環(huán)依賴(A的構(gòu)造方法中依賴B,B的構(gòu)造方法中依賴A),程序會在運(yùn)行時一直循環(huán)調(diào)用最終導(dǎo)致內(nèi)存溢出。

Spring 中三大循環(huán)依賴場景 演示

1.構(gòu)造器注入循環(huán)依賴

@Service
public class A {
    public A(B b) {
    }
}
@Service
public class B {
    public B(A a) {
    }
}

運(yùn)行結(jié)果

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:339)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:215)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)

構(gòu)造器注入構(gòu)成的循環(huán)依賴是無法解決的!只能拋出

BeanCurrentlyInCreationException異常表示循環(huán)依賴。

錯誤原因
根本原因是 Spring 解決循環(huán)依賴依靠的是 Bean 的 中間態(tài) 這個概念,而中間態(tài)指的是 已經(jīng)實(shí)例化,但未初始化的狀態(tài)。而構(gòu)造器負(fù)責(zé)的是實(shí)例化,故無法解決構(gòu)造器循環(huán)依賴。

2.field屬性注入(setter方法注入)循環(huán)依賴

@Service
public class A {
    @Autowired
    private B b;
}

@Service
public class B {
    @Autowired
    private A a;
}

最常用的依賴注入方式,不會出現(xiàn)循環(huán)依賴問題(省略 setter 方式的注入實(shí)現(xiàn)代碼,類似)

3.prototype field 屬性注入循環(huán)依賴

prototype 在平時使用情況較少,仍需重視

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Service
public class A {
    @Autowired
    private B b;
}

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Service
public class B {
    @Autowired
    private A a;
}

此處代碼在運(yùn)行時不會報(bào)錯,因?yàn)?strong>非單例Bean默認(rèn)不會初始化,而是使用時才會初始化。

手動 getBean() 或者在一個單例 Bean 內(nèi) @Autowired A 或 B 就會出錯

// 在單例Bean內(nèi)注入
    @Autowired
    private A a;

注入運(yùn)行結(jié)果

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mytest.TestSpringBean': Unsatisfied dependency expressed through field 'a'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'a': Unsatisfied dependency expressed through field 'b'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'b': Unsatisfied dependency expressed through field 'a'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?

    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374)

錯誤解決方案

使用 @Lazy:@Lazy 只是延遲初始化,當(dāng)真正需要使用到開始初始化時,依舊會出現(xiàn)上面的異常。

Spring 循環(huán)依賴小結(jié)

1.不能解決的情況:

  • 構(gòu)造器注入循環(huán)依賴
  • prototype filed 屬性注入循環(huán)依賴

2.能夠解決的情況:

  • field 屬性注入(setter方法注入)循環(huán)依賴

“三級緩存”意義

Spring 解決循環(huán)依賴原理分析

  • 當(dāng)獲得對象的引用時,對象的屬性是可以延遲設(shè)置的。但是構(gòu)造器必須是在獲取引用之前,引用由構(gòu)造器生成,不生成沒東西引用。
  • Spring 創(chuàng)建 Bean 流程

創(chuàng)建 Bean 的三個核心方法

  • createBeanInstance:例化,即調(diào)用對象的構(gòu)造方法實(shí)例化對象
  • populateBean:填充屬性,主要對 bean 的依賴屬性注入(@Autowired)
  • initializeBean:回到一些如initMethod,InitalizingBean等方法

由初始化 單例Bean 的流程圖可以看出,循環(huán)依賴主要發(fā)生在第二步(populateBean),也就是 field屬性注入的處理

Spring 容器的“三級緩存”

  • Spring 容器的整個生命周期中,單例Bean對象是唯一的。即可以使用緩存來加速訪問
  • Spring 源碼中使用了大量的 Cache 手段,其中在循環(huán)依賴問題的解決過程中就使用了“三級緩存

三級緩存的意義

  • singletonObject:一級緩存,存放完全實(shí)例化且屬性賦值完成的 Bean ,可以直接使用
  • earlySingletonObject:二級緩存,存放早期 Bean 的引用,尚未裝配屬性的 Bean
  • singletonFactories:三級緩存,存放實(shí)例化完成的 Bean 工廠

除了三級緩存,還有另外兩個緩存

  • singletonsCurrentlyInCreation: bean 在創(chuàng)建的過程中都會存儲在此,創(chuàng)建完成移出
  • alreadyCreated:存放至少被創(chuàng)建一次的 bean,不會重復(fù)。即標(biāo)記 bean 是否創(chuàng)建完成

代碼示例

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
	...
	// 從上至下 分表代表這“三級緩存”
	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); // 三級緩存
	...
	
	/** Names of beans that are currently in creation. */
	// 這個緩存也十分重要:它表示bean創(chuàng)建過程中都會在里面呆著~
	// 它在Bean開始創(chuàng)建時放值,創(chuàng)建完成時會將其移出~
	private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

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

源碼解析

  • 單例 Bean 源碼
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
	...
	@Override
	@Nullable
	public Object getSingleton(String beanName) {
		return getSingleton(beanName, true);
	}
	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		//1.先從一級緩存中獲取,獲取到直接返回
		Object singletonObject = this.singletonObjects.get(beanName);
		//2.如果獲取不到或?qū)ο笳趧?chuàng)建,就到二級緩存中去獲取,獲取到直接返回
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);
				//3.如果仍獲取不到,且允許 singletonFactories(allowEarlyCurrentlyInCreation())通過 getObject()獲取。
				//就到三級緩存中用 getObject() 獲取。
				//獲取到就從 singletonFactories中移出,且放進(jìn) earlySingletonObjects。
				//(即從三級緩存移動到二級緩存)
				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;
	}
	...
	public boolean isSingletonCurrentlyInCreation(String beanName) {
		return this.singletonsCurrentlyInCreation.contains(beanName);
	}
	protected boolean isActuallyInCreation(String beanName) {
		return isSingletonCurrentlyInCreation(beanName);
	}
	...
}

加入 singletonFactories 三級緩存的前提是執(zhí)行了構(gòu)造器,所以構(gòu)造器的循環(huán)依賴無法解決。

getSingleton() 從緩存里獲取單例對象步驟分析可知,Spring 解決循環(huán)依賴的重點(diǎn):在于 singletonFactories這個三級緩存。此 Cache 存放著 ObjectFactory,是解決問題的關(guān)鍵。

// 它可以將創(chuàng)建對象的步驟封裝到ObjectFactory中 交給自定義的Scope來選擇是否需要創(chuàng)建對象來靈活的實(shí)現(xiàn)scope。  具體參見Scope接口
@FunctionalInterface
public interface ObjectFactory<T> {
	T getObject() throws BeansException;
}

經(jīng)過 ObjectFactory.geteObject() 后,此時放進(jìn)了 二級緩存 earlySingletonObjects 內(nèi)的這個對象就已經(jīng)實(shí)例化好了,雖然不夠完善,但是該對象的引用 已經(jīng)可以被其他引用了。

補(bǔ)充:

二級緩存 earlySingletonObjects 中的數(shù)據(jù)何時進(jìn)行 添加、刪除?

  • 添加:源碼中的 getSingleton() 里從三級緩存移動到二級緩存中(唯一)。
  • 刪除:addSingleton、addSingletonFactorie、removeSingleton即添加單例、添加單例工廠 ObjectFactory的時候都會刪除二級緩存里面對應(yīng)的緩存值,是互斥的。

總結(jié)

以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringMVC框架REST架構(gòu)體系原理分析

    SpringMVC框架REST架構(gòu)體系原理分析

    REST:Representational State Transfer,資源表現(xiàn)層狀態(tài)轉(zhuǎn)換,是目前&#12112;較主流的&#12032;種互聯(lián)網(wǎng)軟件架構(gòu),它結(jié)構(gòu)清晰、標(biāo)準(zhǔn)規(guī)范、易于理解、便于擴(kuò)展
    2021-09-09
  • Mybatis新手教程之簡單入門

    Mybatis新手教程之簡單入門

    這篇文章主要給大家介紹了關(guān)于Mybatis新手教程之簡單入門的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-02-02
  • @JsonFormat?和?@DateTimeFormat?時間格式化注解(場景示例代碼)

    @JsonFormat?和?@DateTimeFormat?時間格式化注解(場景示例代碼)

    這篇文章主要介紹了@JsonFormat和@DateTimeFormat時間格式化注解,本文通過場景示例代碼詳解給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-05-05
  • Java中synchronized正確使用方法解析

    Java中synchronized正確使用方法解析

    這篇文章主要介紹了Java中syncronized正確使用方法解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-09-09
  • Java格式化輸出詳細(xì)講解(printf、print、println、format等)

    Java格式化輸出詳細(xì)講解(printf、print、println、format等)

    Java的格式化輸出等同于String.Format,與C有很大的相似,下面這篇文章主要給大家介紹了關(guān)于Java格式化輸出(printf、print、println、format等)的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-03-03
  • SpringBoot中使用 RabbitMQ的教程詳解

    SpringBoot中使用 RabbitMQ的教程詳解

    這篇文章主要介紹了SpringBoot中使用 RabbitMQ的教程詳解,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • java實(shí)現(xiàn)簡易撲克牌游戲

    java實(shí)現(xiàn)簡易撲克牌游戲

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡易撲克牌游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-04-04
  • Spring?boot2.0?實(shí)現(xiàn)日志集成的方法(3)

    Spring?boot2.0?實(shí)現(xiàn)日志集成的方法(3)

    這篇文章主要介紹了Spring?boot2.0?實(shí)現(xiàn)日志集成的方法,基于上一篇將日志信息根據(jù)類別輸出到不同的文件中,這篇文章將通過日志來監(jiān)控用戶的操作行為、請求的耗時情況,針對耗時久的請求進(jìn)行性能分析,提升系統(tǒng)性能,需要的小伙伴可以參考一下
    2022-04-04
  • Java 十大排序算法之插入排序刨析

    Java 十大排序算法之插入排序刨析

    插入排序(InsertionSort),一般也被稱為直接插入排序。對于少量元素的排序,它是一個有效的算法。插入排序是一種最簡單的排序方法,它的基本思想是將一個記錄插入到已經(jīng)排好序的有序表中,從而一個新的、記錄數(shù)增 1 的有序表
    2021-11-11
  • SpringSecurity實(shí)現(xiàn)動態(tài)url攔截(基于rbac模型)

    SpringSecurity實(shí)現(xiàn)動態(tài)url攔截(基于rbac模型)

    本文主要介紹了SpringSecurity動態(tài)url攔截,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08

最新評論

民权县| 吴江市| 宁陵县| 同仁县| 达州市| 兰溪市| 焉耆| 高邮市| 遂昌县| 涪陵区| 武功县| 广丰县| 鹤壁市| 乐山市| 青川县| 聂拉木县| 旬邑县| 高要市| 富源县| 辰溪县| 永丰县| 奉贤区| 六枝特区| 永定县| 黄浦区| 广西| 莱阳市| 偃师市| 甘肃省| 察哈| 荣成市| 景东| 富平县| 华池县| 左权县| 富川| 宾川县| 金塔县| 怀仁县| 中卫市| 增城市|