当前位置: 首页 > news >正文

Spring 源码分析(五)——Spring三级缓存的作用分别是什么?

Spring 的三级缓存是经典面试题,也会看到一些文章讲三级缓存与循环依赖之的关系。那么,三级缓存分别存储的什么呢?他们的作用又分别是什么?

一、一、二级缓存

一级缓存是一个名为 singletonObjectsConcurrentHashMap,用于存储已经创建完成的 Bean。其作用也是最明显的,获取 Bean 时最优先从一级缓存中获取。

二级缓存是一个名为 earlySingletonObjectsConcurrentHashMap,用于存储还未创建完成的 Bean。在循环依赖时,可以从中预先获取未创建完成的 Bean,从而解决循环依赖问题。

二、三级缓存

三级缓存是一个名为 singletonFactoriesConcurrentHashMap,用于存储创建指定 Bean 的工厂。其作用是为了在 AOP 代理时解决循环依赖。

更具体可以说,是为了在 AOP 代理时,既解决循环依赖,又最大满足 Spring 制定的 Bean 生命周期流程。

是的,三级缓存一定程度上说,他提前了 Bean 生命周期的流程。

熟悉 Bean 的生命周期可知,Bean 初始化是经过了实例化(createBeanInstance)、属性注入(populateBean)、后置处理器与生命周期方法(initializeBean)三个步骤处理,最终才得到一个创建完成的 Bean

在循环依赖时,在属性注入(populateBean)步骤就要解决依赖问题,而 AOP 代理是在后置处理器与生命周期方法(initializeBean)步骤中通过后置处理器实现的。如果严格按照 Bean 的这个生命周期执行,依赖注入是无法注入代理之后的对象的。

假设,A 和 B 互相依赖,A 经过 AOP 代理,最终的 BeanAa ,那么严格按照 Bean 的生命周期最终属性注入的结果如下:

getBean——取得 A Bean,在 doCreateBean 方法中开始创建 Bean 操作。

createBeanInstance——实例化 A Bean。

populateBean——为 A Bean 设置参数,并调用 getBean 方法创建 B Bean。

== createBeanInstance——实例化 B Bean。

== populateBean——为 B Bean 设置参数,并调用 getBean 方法获得未构造完全的 A Bean。

initializeBean——为A Bean 执行后置处理器和生命周期方法,并完成 AOP 代理,获得最终的 Aa Bean。

从上面步骤可以发现,B 中注入的是代理之前的 A 对象,而实际上需要被注入的是代理之后的 Aa 对象。

要解决上述 AOP 代理的问题其实也很简单,只要在 createBeanInstance 步骤中完成 AOP 代理其实一切问题就迎刃而解。但是这并不符合 Bean 的生命周期设定,而且循环依赖的场景很少见,在实例化时也无法判断这个 Bean 是否被循环依赖了,直接将 AOP 代理全部提前执行不合适。

最终,Spring 引入了三级缓存,在实例化对象之后,进行属性注入之前,将实现 AOP 代理的步骤封装为 Bean 工厂放进三级缓存。如果这个对象被循环依赖了,则使用工厂提前进行 AOP 代理,如果没有被循环依赖,则这个工厂就不会被使用。

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");}// 创建bean工厂注入到三级缓存addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {populateBean(beanName, mbd, instanceWrapper);exposedObject = initializeBean(beanName, exposedObject, mbd);
}

阅读 getEarlyBeanReference 方法逻辑,可以看到其中遍历找到了实现了SmartInstantiationAwareBeanPostProcessor 接口的后置处理器,并执行了其中的 getEarlyBeanReference 方法,提前完成了代理操作,获取到了代理后的最终得对象。

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

在获取对象时,getEarlyBeanReference 便按三级缓存的顺序执行操作,如果在三级缓存中获取到 Bean 工厂,则通过工厂获取对象。

protected Object getSingleton(String beanName, boolean allowEarlyReference) {// Quick check for existing instance without full singleton lockObject singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {// Consistent creation of early reference within full singleton locksingletonObject = 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();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}}}return singletonObject;
}
http://www.lryc.cn/news/177832.html

相关文章:

  • Django基于类视图实现增删改查
  • matplotlib绘图实现中文宋体的两种方法(亲测)
  • 非常有用的JavaScript高阶面试技巧!
  • windows 安装Linux子系统 Ubuntu 并配置python3
  • pytorch的pixel_shuffle转tflite文件
  • sentinel-dashboard-1.8.0.jar开机自启动脚本
  • c++堆排序-建堆-插入-删除-排序
  • 使用代理后pip install 出现ssl错误
  • 护眼灯什么价位的好?最具性价比的护眼台灯推荐
  • vue event bus 事件总线
  • 深信服云桌面用户忘记密码后的处理
  • Cocos Creator3.8 实战问题(一)cocos creator prefab 无法显示内容
  • 朴素贝叶斯深度解码:从原理到深度学习应用
  • RUST 每日一省:闭包
  • Ubuntu下文件的解压缩操作:常用zip和unzip
  • Linux学习第22天:Linux中断驱动开发(一): 突如其来
  • IDEA 2019 Springboot 3.1.3 运行异常
  • 【JAVA】飞机大战
  • Midjourney 生成油画技巧
  • 26559-2021 机械式停车设备 分类
  • xxe攻击(XML外部实体)
  • 大数据-hadoop
  • 容器启动报错
  • 求生之路2服务器搭建插件安装及详细的游戏参数配置教程linux
  • IntelliJ IDEA 左侧Commit栏不见了
  • 使用自功率谱、互功率谱估计滤波器幅频特性
  • 51单片机光照强度检测自动路灯开关仿真( proteus仿真+程序+报告+讲解视频)
  • socat管理haproxy配置
  • Linux发行版X华为鲲鹏openEuler
  • 计算机网络相关知识点