news 2026/4/18 4:08:57

Spring 三级缓存详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring 三级缓存详解

Spring 的三级缓存是解决单例Bean循环依赖的核心机制。理解三级缓存对于掌握Spring的Bean创建过程至关重要。

一、三级缓存定义与作用

三级缓存的含义

// 在 DefaultSingletonBeanRegistry 中定义 public class DefaultSingletonBeanRegistry extends ... { // 一级缓存:存放完全初始化好的Bean private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 二级缓存:存放早期暴露的Bean(已实例化但未完全初始化) private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16); // 三级缓存:存放Bean工厂,用于创建早期引用 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); }

各级缓存的作用

缓存级别名称存储内容作用
一级缓存singletonObjects完全初始化好的单例Bean缓存最终可用的Bean
二级缓存earlySingletonObjects早期Bean(半成品)解决循环依赖,避免重复创建代理
三级缓存singletonFactoriesObjectFactory(Bean工厂)创建早期引用,支持AOP等扩展

二、循环依赖场景分析

场景一:最简单的循环依赖

@Component public class A { @Autowired private B b; } @Component public class B { @Autowired private A a; }

场景二:自我依赖(少见但能说明问题)

@Component public class SelfRefBean { @Autowired private SelfRefBean self; }

三、三级缓存解决循环依赖的完整流程

步骤详解(以A↔B循环依赖为例)

// 模拟三级缓存解决循环依赖的过程 public class ThreeLevelCacheDemo { public static void main(String[] args) { // 创建过程模拟 createBeanA(); } static void createBeanA() { System.out.println("1. 开始创建Bean A"); // Step 1: 实例化A(调用构造函数) Object a = new A(); System.out.println("2. A实例化完成,将A的ObjectFactory放入三级缓存"); // 三级缓存:singletonFactories.put("a", () -> getEarlyBeanReference("a", a)); // Step 2: 属性填充,发现需要B System.out.println("3. 开始填充A的属性,发现需要B"); // Step 3: 创建B createBeanB(); // Step 6: 完成A的初始化 System.out.println("9. B创建完成,继续完成A的初始化"); System.out.println("10. 将A从二级缓存移除,放入一级缓存"); // 一级缓存:singletonObjects.put("a", a); // 二级缓存:earlySingletonObjects.remove("a"); } static void createBeanB() { System.out.println("4. 开始创建Bean B"); // Step 3: 实例化B Object b = new B(); System.out.println("5. B实例化完成,将B的ObjectFactory放入三级缓存"); // 三级缓存:singletonFactories.put("b", () -> getEarlyBeanReference("b", b)); // Step 4: 属性填充,发现需要A System.out.println("6. 开始填充B的属性,发现需要A"); // Step 5: 获取A(从三级缓存获取早期引用) System.out.println("7. 从三级缓存获取A的早期引用"); // Object earlyA = singletonFactories.get("a").getObject(); System.out.println("8. 将A放入二级缓存,从三级缓存移除A的工厂"); // 二级缓存:earlySingletonObjects.put("a", earlyA); // 三级缓存:singletonFactories.remove("a"); // 将earlyA注入到B System.out.println("9. 将A的早期引用注入B,完成B的初始化"); System.out.println("10. 将B从二级缓存移除,放入一级缓存"); // 一级缓存:singletonObjects.put("b", b); // 二级缓存:earlySingletonObjects.remove("b"); } }

流程图解

创建A → 实例化A → 将A工厂放入三级缓存 ↓ 填充A属性 → 需要B → 创建B ↓ 实例化B → 将B工厂放入三级缓存 ↓ 填充B属性 → 需要A → 从三级缓存获取A工厂 ↓ ↓ 创建A早期引用 ← 执行A工厂getObject() ↓ 将A早期引用放入二级缓存,移除三级缓存中的A工厂 ↓ 将A早期引用注入B → 完成B初始化 ↓ 将B放入一级缓存 → 返回B ↓ 获取到B → 注入B到A → 完成A初始化 ↓ 将A放入一级缓存,移除二级缓存中的A

四、三级缓存源码深度解析

1. 获取Bean的核心方法

protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 1. 先从一级缓存获取 Object singletonObject = this.singletonObjects.get(beanName); // 如果一级缓存没有,且Bean正在创建中(解决循环依赖) if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // 2. 从二级缓存获取 singletonObject = this.earlySingletonObjects.get(beanName); // 如果二级缓存没有,且允许早期引用 if (singletonObject == null && allowEarlyReference) { // 3. 从三级缓存获取Bean工厂 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // 通过工厂创建早期Bean singletonObject = singletonFactory.getObject(); // 放入二级缓存 this.earlySingletonObjects.put(beanName, singletonObject); // 移除三级缓存中的工厂 this.singletonFactories.remove(beanName); } } } } return singletonObject; }

2. Bean创建过程中的缓存操作

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) { // 1. 实例化Bean BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args); Object bean = instanceWrapper.getWrappedInstance(); // 2. 判断是否支持早期暴露(单例、允许循环引用) boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { // 3. 将Bean工厂添加到三级缓存 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // 4. 属性填充(可能触发循环依赖) populateBean(beanName, mbd, instanceWrapper); // 5. 初始化 Object exposedObject = initializeBean(beanName, exposedObject, mbd); // 6. 处理早期引用 if (earlySingletonExposure) { // 从一级缓存获取(检查是否已被其他Bean初始化过程修改) Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { // 如果exposedObject没有被增强,使用早期引用 if (exposedObject == bean) { exposedObject = earlySingletonReference; } } } return exposedObject; } // 添加工厂到三级缓存 protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { // 放入三级缓存 this.singletonFactories.put(beanName, singletonFactory); // 清除二级缓存(确保从工厂创建) this.earlySingletonObjects.remove(beanName); // 记录已注册的单例 this.registeredSingletons.add(beanName); } } }

3. 获取早期Bean引用(支持AOP)

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; // 1. 如果Bean需要被后处理器增强(如AOP) if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; // 2. 获取早期引用(可能是代理对象) exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } } return exposedObject; }

五、为什么需要三级缓存?二级不够吗?

场景分析:AOP代理的循环依赖

@Component public class A { @Autowired private B b; public void doSomething() { System.out.println("A do something"); } } @Component public class B { @Autowired private A a; // 这里期望注入的是A的代理对象,而不是原始对象 public void test() { // 如果注入的是原始对象,AOP增强会失效 a.doSomething(); } } @Aspect @Component public class LogAspect { @Before("execution(* com.example.A.doSomething(..))") public void logBefore() { System.out.println("Log before method execution"); } }

三级缓存的必要性

  1. 延迟代理创建:三级缓存存储的是ObjectFactory,可以延迟决定何时以及如何创建代理

  2. 保证代理一致性:确保所有Bean注入的是同一个代理实例

  3. 避免重复创建代理:如果没有三级缓存,每次获取早期引用都可能创建新的代理

如果只有二级缓存的问题

// 假设只有二级缓存 public Object getSingleton(String beanName) { Object bean = singletonObjects.get(beanName); if (bean == null && isSingletonCurrentlyInCreation(beanName)) { // 只有二级缓存:直接创建早期引用 bean = createEarlyBeanReference(beanName); earlySingletonObjects.put(beanName, bean); } return bean; } // 问题:如果多个地方同时获取早期引用,可能创建多个不同的代理实例 // 特别是当A需要被AOP增强时

六、特殊场景处理

1. 构造器循环依赖(无法解决)

@Component public class ConstructorA { private ConstructorB b; @Autowired public ConstructorA(ConstructorB b) { // 构造器注入 this.b = b; } } @Component public class ConstructorB { private ConstructorA a; @Autowired public ConstructorB(ConstructorA a) { // 构造器注入 this.a = a; } } // 抛出 BeanCurrentlyInCreationException

原因:构造器注入时,Bean还未实例化,无法放入三级缓存。

2. 原型Bean的循环依赖(无法解决)

@Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class PrototypeA { @Autowired private PrototypeB b; } @Component @Scope(ConfigurableBeanFactory.SCOPE_PROTotype) public class PrototypeB { @Autowired private PrototypeA a; } // 抛出 BeanCurrentlyInCreationException

原因:原型Bean不缓存,每次都是新创建,Spring不支持原型Bean的循环依赖。

3. @Async注解的循环依赖

@Component public class AsyncA { @Autowired private AsyncB b; @Async public void asyncMethod() { // 异步方法 } } @Component public class AsyncB { @Autowired private AsyncA a; // 这里注入的可能是原始对象,而不是代理 }

解决方法:使用@Lazy注解

@Component public class AsyncB { @Lazy @Autowired private AsyncA a; }

七、三级缓存与AOP的协作

代理创建时机

// AbstractAutoProxyCreator(AOP核心类)中的处理 public Object getEarlyBeanReference(Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); // 将原始Bean和代理关系缓存起来 this.earlyProxyReferences.put(cacheKey, bean); // 创建代理 return wrapIfNecessary(bean, beanName, cacheKey); } // 后续初始化完成后检查 public Object postProcessAfterInitialization(Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); // 如果早期已经创建过代理,直接返回早期代理 if (this.earlyProxyReferences.remove(cacheKey) != bean) { // 否则检查是否需要创建代理 return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }

八、性能与线程安全考虑

1. 缓存访问的同步

// DefaultSingletonBeanRegistry 中的同步控制 protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { // 以一级缓存为锁 this.singletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }

2. 性能优化

  • 一级缓存使用ConcurrentHashMap,支持高并发读

  • 二级缓存使用ConcurrentHashMap,但实际访问需要加锁

  • 三级缓存使用HashMap,因为只在创建Bean时访问,且需要同步

九、实际调试技巧

1. 查看三级缓存状态

@SpringBootTest public class CacheDebugTest { @Autowired private ApplicationContext applicationContext; @Test public void debugThreeLevelCache() throws Exception { DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry) ((AbstractApplicationContext) applicationContext).getBeanFactory(); // 通过反射查看缓存内容 Field singletonObjectsField = DefaultSingletonBeanRegistry.class .getDeclaredField("singletonObjects"); singletonObjectsField.setAccessible(true); Map<String, Object> singletonObjects = (Map<String, Object>) singletonObjectsField.get(registry); System.out.println("一级缓存大小: " + singletonObjects.size()); // 类似方式可以查看二级和三级缓存 } }

2. 循环依赖调试配置

# application.properties # 开启循环依赖调试日志 logging.level.org.springframework.beans.factory.support=DEBUG # 关闭Spring的循环依赖快速失败(仅用于调试) spring.main.allow-circular-references=true

十、最佳实践与注意事项

1. 避免循环依赖

  • 优先使用构造器注入:强制在编译期发现循环依赖

  • 代码重构:提取公共逻辑到第三个类中

  • 使用@Lazy注解:延迟加载打破循环

2. 设计建议

// 不好的设计:双向紧密耦合 @Service public class OrderService { @Autowired private UserService userService; } @Service public class UserService { @Autowired private OrderService orderService; } // 好的设计:提取公共逻辑 @Service public class OrderService { @Autowired private CommonService commonService; } @Service public class UserService { @Autowired private CommonService commonService; } @Service public class CommonService { // 公共业务逻辑 }

3. 配置建议

@Configuration public class AppConfig { // 如果不使用AOP,可以关闭循环引用支持提升性能 @Bean public static BeanFactoryPostProcessor disableCircularReferences() { return beanFactory -> { if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowCircularReferences(false); } }; } }

总结

Spring三级缓存的核心价值:

  1. 解决循环依赖:通过提前暴露Bean引用

  2. 支持AOP:确保注入的是正确的代理对象

  3. 保证单例唯一性:避免重复创建Bean实例

  4. 性能优化:减少不必要的Bean创建

理解三级缓存不仅有助于解决循环依赖问题,更能深入理解Spring容器的设计哲学。在实际开发中,虽然三级缓存解决了技术问题,但良好的设计应该尽量避免循环依赖的出现。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 3:31:11

大数据领域日志数据的存储优化与性能调优

大数据领域日志数据的存储优化与性能调优关键词&#xff1a;大数据、日志数据、存储优化、性能调优、数据处理摘要&#xff1a;本文围绕大数据领域中日志数据的存储优化与性能调优展开。首先介绍了相关背景知识&#xff0c;包括目的、预期读者等。接着详细解释了日志数据存储和…

作者头像 李华
网站建设 2026/4/18 3:31:26

【毕业设计】机器学习基于cnn卷积网络识别树叶是否存在病变

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/18 3:37:30

交互式世界建模新方案!腾讯混元发布世界模型WorldPlay,兼顾实时生成与长期几何一致性;5万条样本!Med-Banana-50K支持增删病灶双向编辑

世界模型正驱动计算智能领域的重心逐渐从语言任务拓展至视觉与空间推理&#xff0c;通过构建动态三维环境的模拟能力&#xff0c;这些模型使智能体得以感知并与复杂场景交互&#xff0c;从而为具身智能、游戏开发等领域开启了新的研究与应用前景。世界模型的前沿正聚焦于实时交…

作者头像 李华
网站建设 2026/4/18 3:33:04

深度学习毕设选题推荐:人工智能基于python的鲜花识别

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/18 2:23:35

【论文阅读】Steering Your Diffusion Policy with Latent Space Reinforcement Learning

【论文阅读】Steering Your Diffusion Policy with Latent Space Reinforcement Learning1 团队与发表时间2. 问题背景与核心思路3. 具体做法3.1 模型设计3.2 Loss 设计3.3 数据设计4 实验效果5 结论6 扩散模型进行RL的方案6.1 纯离线设置 (Purely Offline Setting)6.2 在线设置…

作者头像 李华
网站建设 2026/4/18 3:31:09

深度测评专科生必用的8款AI论文软件

深度测评专科生必用的8款AI论文软件 2026年专科生论文写作工具测评&#xff1a;如何选择适合自己的AI助手 随着AI技术的不断进步&#xff0c;越来越多的专科生开始借助AI工具提升论文写作效率。然而&#xff0c;面对市场上琳琅满目的论文辅助软件&#xff0c;如何挑选真正适合自…

作者头像 李华