Spring三级缓存详解

news/2024/10/30 19:34:42/

Spring三级缓存是为了解决对象间的循环依赖问题。

A依赖B,B依赖A,这就是一个简单的循环依赖。

我们来先看看三级缓存的源码。

(1)查看“获取Bean”的源码,注意getSingleton()方法。

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {//第1级缓存 用于存放 已经属性赋值、完成初始化的 单列BEANprivate final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);//第2级缓存 用于存在已经实例化,还未做代理属性赋值操作的 单例BEANprivate final Map<String, Object> earlySingletonObjects = new HashMap<>(16);//第3级缓存 存储创建单例BEAN的工厂private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);//已经注册的单例池里的beanNameprivate final Set<String> registeredSingletons = new LinkedHashSet<>(256);//正在创建中的beanName集合private final Set<String> singletonsCurrentlyInCreation =Collections.newSetFromMap(new ConcurrentHashMap<>(16));//缓存查找bean  如果第1级缓存没有,那么从第2级缓存获取。如果第2级缓存也没有,那么从第3级缓存创建,并放入第2级缓存。protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName); //第1级if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {singletonObject = this.earlySingletonObjects.get(beanName); //第2级if (singletonObject == null && allowEarlyReference) {//第3级缓存  在doCreateBean中创建了bean的实例后,封装ObjectFactory放入缓存的bean实例ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {//创建未赋值的beansingletonObject = singletonFactory.getObject();//放入到第2级缓存this.earlySingletonObjects.put(beanName, singletonObject);//从第3级缓存删除this.singletonFactories.remove(beanName);}}}}return singletonObject;}   }

(2)“添加到第1级缓存”的源码:

 protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {// 放入第1级缓存this.singletonObjects.put(beanName, singletonObject);// 从第3级缓存删除this.singletonFactories.remove(beanName);// 从第2级缓存删除this.earlySingletonObjects.remove(beanName);// 放入已注册的单例池里this.registeredSingletons.add(beanName);}}

(3)“添加到第3级缓存”的源码:

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {synchronized (this.singletonObjects) {// 若第1级缓存没有bean实例if (!this.singletonObjects.containsKey(beanName)) {// 放入第3级缓存this.singletonFactories.put(beanName, singletonFactory);// 从第2级缓存删除,确保第2级缓存没有该beanthis.earlySingletonObjects.remove(beanName);// 放入已注册的单例池里this.registeredSingletons.add(beanName);}}}

(4)“创建Bean”的源码:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, Object[] args) throws BeanCreationException {BeanWrapper instanceWrapper = null;if (instanceWrapper == null) {//实例化对象instanceWrapper = this.createBeanInstance(beanName, mbd, args);}final Object bean = instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null;Class<?> beanType = instanceWrapper != null ? instanceWrapper.getWrappedClass() : null;//判断是否允许提前暴露对象,如果允许,则直接添加一个 ObjectFactory 到第3级缓存boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {//添加到第3级缓存addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}//填充属性this.populateBean(beanName, mbd, instanceWrapper);//执行初始化方法,并创建代理exposedObject = initializeBean(beanName, exposedObject, mbd);return exposedObject;
}

通过这段代码,我们可以知道:Spring 在实例化对象之后,就会为其创建一个 Bean 工厂,并将此工厂加入到三级缓存中。

因此,Spring 一开始提前暴露的并不是实例化的 Bean,而是将 Bean 包装起来的ObjectFactory。为什么要这么做呢?

这实际上涉及到 AOP。如果创建的 Bean 是有代理的,那么注入的就应该是代理 Bean,而不是原始的 Bean。但是,Spring一开始并不知道 Bean是否会有循环依赖,通常情况下(没有循环依赖的情况下),Spring 都会在“完成填充属性并且执行完初始化方法”之后再为其创建代理。但是,如果出现了循环依赖,Spring 就不得不为其提前创建"代理对象";否则,注入的就是一个原始对象,而不是代理对象。因此,这里就涉及到"应该在哪里提前创建代理对象"?

Spring 的做法就是:在 ObjectFactory 中去提前创建代理对象。它会执行 getObject() 方法来获取到 Bean。实际上,它真正执行的方法如下:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean;if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;// 如果需要代理,这里会返回代理对象;否则,返回原始对象。exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);}}}return exposedObject;
}

提前进行对象的代理工作,并在 earlyProxyReferences map中记录已被代理的对象,是为了避免在后面重复创建代理对象。

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupportimplements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {@Overridepublic Object getEarlyBeanReference(Object bean, String beanName) {Object cacheKey = getCacheKey(bean.getClass(), beanName);// 记录已被代理的对象this.earlyProxyReferences.put(cacheKey, bean);return wrapIfNecessary(bean, beanName, cacheKey);}
}

再次分析获取bean的方法getSingleton()方法,可知:

提前暴露的对象,虽然已实例化,但是没有进行属性填充,还没有完成初始化,是一个不完整的对象。 这个对象存放在二级缓存中,对于三级缓存机制十分重要,是解决循环依赖一个非常巧妙的设计。

让我们来分析一下“A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情景。

  1. A 调用doCreateBean()创建Bean对象:由于还未创建,从第1级缓存singletonObjects查不到,此时只是一个半成品(提前暴露的对象),放入第3级缓存singletonFactories。
  2. A在属性填充时发现自己需要B对象,但是在三级缓存中均未发现B,于是创建B的半成品,放入第3级缓存singletonFactories。
  3. B在属性填充时发现自己需要A对象,从第1级缓存singletonObjects和第2级缓存earlySingletonObjects中未发现A,但是在第3级缓存singletonFactories中发现A,将A放入第2级缓存earlySingletonObjects,同时从第3级缓存singletonFactories删除。
  4. 将A注入到对象B中。
  5. B完成属性填充,执行初始化方法,将自己放入第1级缓存singletonObjects中(此时B是一个完整的对象),同时从第3级缓存singletonFactories和第2级缓存earlySingletonObjects中删除。
  6. A得到“对象B的完整实例”,将B注入到A中。
  7. A完成属性填充,执行初始化方法,并放入到第1级缓存singletonObjects中。
     

在创建过程中,都是从第三级缓存(对象工厂创建不完整对象),将提前暴露的对象放入到第二级缓存;从第二级缓存拿到后,完成初始化,并放入第一级缓存。


http://www.ppmy.cn/news/148759.html

相关文章

智能网联环境下数据安全分析与建设思考

01安全概况 随着智能化程度不断提升&#xff0c;汽车正逐渐从传统意义上的交通工具演化为公路上的智能终端&#xff0c;核心部件也从发动机、变速箱、底盘上升为芯片、软件与数据。相关机构研究表明&#xff0c;目前全球市场搭载智能网联功能的新车渗透率约为45%&#xff0c;预…

微信小程序——CSS限制文字宽度和行数(溢出显示省略号)

手把手教你学会判断用户在做向上滑动还是向下滑动 知识回调&#xff08;不懂就看这儿&#xff01;&#xff09;场景复现核心干货单行文本溢出显示省略号多行文本溢出显示省略号 知识回调&#xff08;不懂就看这儿&#xff01;&#xff09; 知识专栏专栏链接微信小程序专栏http…

Spring Boot如何实现分布式事务的协调和管理

Spring Boot如何实现分布式事务的协调和管理 在分布式系统中&#xff0c;事务是非常重要的一部分&#xff0c;可以保证多个操作在一个原子性的操作中完成&#xff0c;确保数据的一致性和可靠性。在分布式系统中&#xff0c;分布式事务需要考虑多个服务之间的数据一致性和事务提…

面试+学习+做项目+最全Java视频讲解

关注公众号&#xff1a;计算机视觉与图形学实战 Java零基础教程视频&#xff08;适合Java 0基础&#xff0c;Java初学入门&#xff09; 尚硅谷Java零基础入门教程&#xff08;含百道Java真题&#xff0c;2万多行Java代码实战&#xff09; 黑马程序员全套Java教程_Java基础入门视…

梦源虚拟示波器U2P20介绍、拆机

介绍 拆机 背面 正面 W9825G6KH-6&#xff1a;SDRAM 32MB XC6SLX9-TQG144BIV2029&#xff1a;FPGA CY7C68013A &#xff1a;集成了USB2.0的MCU 4128BRP &#xff1a;EEPROM MXT2088&#xff1a;AD9288的国产替代芯片。

ORA-12801

今天在并行查询时报了ORA-12801错误&#xff0c;查询语句很简单&#xff1a;select/*parallel(t 16)*/count(1) from a 看看官方解释&#xff1a; ORA-12801: error signaled in parallel query server string Cause: A parallel query server reached an exception condition…

thinkpad T420 安装Windows server 2003 备忘

因工作原因&#xff0c;需要在thingkpad t420 本上安装 Windows server 2003 操作系统。在网上查到了一些资料&#xff0c;借鉴了网友xp系统的一些方法安装成功&#xff0c;以防遗忘&#xff0c;也分享给需要的朋友&#xff0c;总结如下&#xff1a; 1、F1 进入cmos&#xff0…

联想服务器开启虚拟化,联想电脑虚拟化开启方法

适用范围: 联想电脑 操作步骤: 1、Intel平台笔记本&#xff1a; 进入BIOS&#xff0c;选择configuration的选项&#xff0c;找到Intel Virtual Technology&#xff0c;改成Enable的开启状态。 2、AMD平台的笔记本&#xff1a; 进入BIOS&#xff0c;选择configuration的选项&a…