Spring如何使用三级缓存解决循环依赖

Spring如何使用三级缓存解决循环依赖

为什么说Spring使用三级缓存?

这里说的三级缓存指Spring在创建单例 bean 的时候使用三层缓存来解决循环依赖。
直接看代码 org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java

	@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;
	}

核心思路很简单,将创建完成的单例全部放入缓存中,后续再次创建时先从缓存中获取,最终保证同一个类只创建一次。

能否只用一级缓存?

如果只用一级缓存,我们需要在创建 bean 后立刻将其放入缓存中,后续再次加载该 bean 时直接使用缓存中的 bean。

思考一下有没有什么问题?
🤔
🤔
🤔
🤔
🤔
🤔
🤔
🤔
😯:从缓存中获取的 bean 可能并未完成初始化,部分场景下是否会造成空指针等异常?

怎么办

引入二级缓存

为了区分开正在初始化初始化完成的 bean ,我们需要引入二级缓存,新创建的 bean 先放入二级缓存中,待初始化完成后再加入到一级缓存中。
当我们遇到需要拿到 bean 后立刻使用的场景,就只从一级缓存中获取。

什么?还有问题 ?

能否支持 bean 的动态代理?

理一理流程:

  1. 创建 bean 实例
  2. 扔入二级缓存中
  3. 进行初始化,生成 bean 动态代理
  4. 将代理扔入一级缓存并删除二级缓存

woc!!
二级缓存和一级缓存中的 bean 并不相同,如果存在外部引用已经指向了二级缓存中的 bean 怎么办?
我们知道在动态代理场景下,对 bean 的引用实际应该指向其代理,否则无法使用代理功能。比如 AOP 就需要依赖代理。

使用三级缓存

第三级缓存与其他两级不太相同,第三级存储的是 bean 的创建工厂。
org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java
在这里插入图片描述
当 bean 实例创建完成后为其生成一个专属工厂实例,并放入三级缓存中。
org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
在这里插入图片描述
org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java
在这里插入图片描述
这里 getEarlyBeanReference 会调用 SmartInstantiationAwareBeanPostProcessor 接口去生成一个 bean 的早期引用,如果 bean 需要被代理,getEarlyBeanReference 将提前为 bean 生成一个代理对象。
在 bean 初始化过程中遇到外部引用,则将 getEarlyBeanReference 生成的代理类扔入二级缓存,后续在 bean 的初始化过程中,将延用该代理对象,不再生成新代理。
通过上述方式引入三级缓存后,满足了动态代理的场景。

简单总结

一级缓存
用于缓存所有初始化完成的单例 bean

二级缓存
缓存创建完实例但未初始化的单例 bean

三级缓存
缓存 ObjectFactory,使用 getEarlyBeanReference 方法在代理场景下根据 bean 提前生成其代理对象,保证需要代理的 bean 在初始化过程中,其代理能正确地被放入二级缓存。

最后,给大家提个醒

在我们自己实现 BeanPostProcessor 去扩展代理时,最好直接实现SmartInstantiationAwareBeanPostProcessor ,并重写 getEarlyBeanReference 方法。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值