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

【白话Spring】三级缓存

快速导航
  • 一、Spring的三级缓存是什么?
    • 三级缓存
    • SpringBean 的生命周期:
      • BeanFactory关于Bean初始化注释:
      • 分析:
        • Bean的创建过程:
        • Bean的销毁过程:
      • Spring Bean创建的核心逻辑:
  • 二、Spring的三级缓存解决了什么问题?
    • 1.循环依赖问题
    • 2.支持Spring的AOP

一、Spring的三级缓存是什么?

三级缓存

简单来说,就是map,和我们在开发中new的map没有任何区别。

/** Cache of singleton objects: bean name to bean instance. */
// 一级缓存:存储完整的单例对象。key是对象名称,value是对象实例。
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name to ObjectFactory. */
// 三级缓存:存储生成单例对象的工厂对象。key是对象名称,value是工厂实例。
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** Cache of early singleton objects: bean name to bean instance. */
// 二级缓存:存储实例化后的对象(对象未注入属性和初始化)。key是对象名称,value是对象实例。
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

从上面的代码中可以看出,三级缓存就是3个全局的Map。

只是一级缓存和二级缓存使用的是线程安全的容器 ConcurrentHashMap 保存。

这里提两个问题,大家可以思考一下:
1.这里的三个容器变量名为什么都带着singleton?
2.为什么一级缓存和二级缓存使用的ConcurrentHashMap,而三级缓存使用HashMap

容器只是个工具,怎么用才是我们需要了解的关键,什么时候给容器里存入Bean,把Bean存入哪个容器,什么时候清空容器,这就不得不了解一下Spring中Bean的生命周期了。

SpringBean 的生命周期:

BeanFactory关于Bean初始化注释:
  1. BeanNameAware’s setBeanName 设置实例名称
  2. BeanClassLoaderAware’s setBeanClassLoader 设置实例类加载器
  3. BeanFactoryAware’s setBeanFactory 设置Bean工厂
  4. EnvironmentAware’s setEnvironment 设置环境
  5. EmbeddedValueResolverAware’s setEmbeddedValueResolver 设置内嵌的值解析器
  6. ResourceLoaderAware’s setResourceLoader (only applicable when running in an application context)
  7. ApplicationEventPublisherAware’s setApplicationEventPublisher (only applicable when running in an application context)
  8. MessageSourceAware’s setMessageSource (only applicable when running in an application context)
  9. ApplicationContextAware’s setApplicationContext (only applicable when running in an application context)
  10. ServletContextAware’s setServletContext (only applicable when running in a web application context)
  11. postProcessBeforeInitialization methods of BeanPostProcessors
  12. InitializingBean’s afterPropertiesSet
  13. a custom init-method definition
  14. postProcessAfterInitialization methods of BeanPostProcessors

On shutdown of a bean factory, the following lifecycle methods apply:

  1. postProcessBeforeDestruction methods of DestructionAwareBeanPostProcessors
  2. DisposableBean’s destroy
  3. a custom destroy-method definition
分析:
Bean的创建过程:
  1. 前面10个都是 xxxAware.setXXX()方法
  2. 然后就是 BeanPostProcessors.postProcessBeforeInitialization()
  3. InitializingBean.afterPropertiesSet()
  4. 自定义的init()方法
  5. BeanPostProcessors.postProcessAfterInitialization()
Bean的销毁过程:
  1. DestructionAwareBeanPostProcessors.postProcessBeforeDestruction()
  2. DisposableBean.destroy()
  3. 自定义的destroy()方法
Spring Bean创建的核心逻辑:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

  1. createBeanInstance()实例化Bean
    Bean实例化
  2. populateBean() 填充属性和 initializeBean() 初始化
    Bean属性填充和初始化
  3. 进入initializeBean()
    初始化过程
  4. invokeAwareMethods()源码
    在这里插入图片描述
  5. applyBeanPostProcessorsBeforeInitialization()
    在这里插入图片描述
  6. invokeInitMethods()
    在这里插入图片描述7. applyBeanPostProcessorsAfterInitialization()在这里插入图片描述

二、Spring的三级缓存解决了什么问题?

1.循环依赖问题

设想一下没有三级缓存的循环依赖问题:ServiceA依赖ServiceB,ServiceB依赖ServiceA

  1. ServiceA实例化,然后在属性填充的时候,发现依赖ServiceB。在Spring容器中找ServiceB,没有找到。
  2. ServiceA暂停属性注入,开始实例化ServiceB,然后在属性填充的时候,发现依赖ServiceA,于是又在Spring容器中找ServiceA,同样没有找到。
  3. GG了。

加入了三级缓存:

  1. ServiceA实例化,之后将实例化的不完整的实例ServiceA放入三级缓存。然后在属性填充的时候,发现ServiceA依赖ServiceB。在Spring容器中的一级缓存、二级缓存和三级缓存中找ServiceB,没有找到。
  2. ServiceA暂停属性注入,开始实例化ServiceB,同样将不完整的ServixcB实例放入三级缓存。然后在属性填充的时候,发现依赖ServiceA,于是又在Spring容器中找ServiceA,先找了一级缓存和二级缓存,没找到。在三级缓存中找到了不完整的实例ServiceA,然后将ServiceB从三级缓冲中移除,放入二级缓存,然后成功的对ServiceB进行了属性填充和初始化操作,然后从二级缓存移除,放入一级缓存。
  3. ServiceA继续属性注入,依次放入二级缓存和一级缓存。

问题:
解决循环依赖的问题有两级缓存就够了,为什么要用三级缓存呢?
答:因为Spring需要支持AOP

2.支持Spring的AOP

在Bean被AOP进行了切面代理之后,三级缓存中的singletonFactory获取到的对象实例是目标Bean的一个代理对象。
每次获取到的都是新的代理对象,就破坏了Spring解决循环依赖问题的基础,即所有的对象都是单例的。
而加入了二级缓存以后,代理对象也是只获取一次,然后放入二级缓存备用。

参考资料:

https://topjavaer.cn/advance/excellent-article/6-spring-three-cache.html#%E4%B8%89%E7%BA%A7%E7%BC%93%E5%AD%98%E8%A7%A3%E5%86%B3%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96

https://www.mianshiya.com/bank/1790683494127804418/question/1780933295387734017


http://www.kler.cn/a/551265.html

相关文章:

  • 【C语言】有序数组的平方
  • 面试真题 | 招银 C++
  • 阿里4面+腾讯4面春招面试题解析,附Java 岗 988 道题分享
  • SQL注入(SQL Injection)详解与实战
  • LVS 负载均衡集群(DR 模式)
  • ThreadLocal为什么会内存溢出
  • 【deepseek之我问】如何把AI技术与教育相结合,适龄教育,九年义务教育,以及大学教育,更着重英语学习。如何结合,给出观点。结合最新智能体Deepseek
  • 【Docker】ollama部署deepseek
  • HDFS是如何存储和管理大数据
  • 移动通信发展史
  • 【个人开发】deepspeed+Llama-factory 本地数据多卡Lora微调【完整教程】
  • mysql多主集群 galera cluster for mysql 8安装配置启动重启集群
  • 动手实现自己的 JVM——Go!(ch03)
  • 烧烤炉出口亚马逊欧盟站CE认证EN1860安全标准
  • Golang的并发编程案例详解
  • LeetCode1287
  • 【SpringBoot苍穹外卖】debugDay04
  • TikTok 多账号管理与自动化运营:矩阵系统功能全解析
  • 国产编辑器EverEdit - 括号匹配检查
  • Unity DeepSeek API 聊天接入教程(0基础教学)