# Spring三级缓存机制学习笔记 ## 📚 概述 Spring三级缓存是Spring框架用来解决**单例Bean循环依赖**问题的核心机制。它通过三个不同的缓存Map来存储Bean在不同生命周期阶段的状态。 --- ## 🎯 学习目标 - [ ] 理解什么是循环依赖问题 - [ ] 掌握三级缓存的结构和作用 - [ ] 理解循环依赖的解决流程 - [ ] 学会分析相关源码 --- ## 🔍 核心概念 ### 什么是循环依赖? 当两个或多个Bean相互依赖时形成的依赖环: ```java @Component public class ServiceA { @Autowired private ServiceB serviceB; // A依赖B } @Component public class ServiceB { @Autowired private ServiceA serviceA; // B依赖A } ``` **问题**:如果没有特殊处理,会陷入无限循环创建对象的死循环。 --- ## 🏗️ 三级缓存结构 Spring使用三个Map来管理Bean的不同状态: ```java /** 一级缓存:完整的单例Bean */ private final Map singletonObjects = new ConcurrentHashMap<>(256); /** 二级缓存:早期暴露的Bean对象 */ private final Map earlySingletonObjects = new HashMap<>(16); /** 三级缓存:Bean工厂对象 */ private final Map> singletonFactories = new HashMap<>(16); ``` ### 各级缓存详解 | 缓存级别 | 名称 | 作用 | 存储内容 | |---------|------|------|---------| | **一级缓存** | singletonObjects | 成品仓库 | 完全初始化完成的Bean | | **二级缓存** | earlySingletonObjects | 半成品仓库 | 实例化但未完成依赖注入的Bean | | **三级缓存** | singletonFactories | 工厂仓库 | Bean的ObjectFactory,支持AOP代理 | --- ## 🔄 循环依赖解决流程 以ServiceA ↔ ServiceB循环依赖为例: ### 流程图 ``` 1. 创建ServiceA ↓ 2. ServiceA实例化 → 放入三级缓存 ↓ 3. ServiceA需要ServiceB → 开始创建ServiceB ↓ 4. ServiceB实例化 → 放入三级缓存 ↓ 5. ServiceB需要ServiceA → 从缓存获取ServiceA早期引用 ↓ 6. ServiceA从三级缓存移至二级缓存 ↓ 7. ServiceB完成创建 → 放入一级缓存 ↓ 8. ServiceA完成创建 → 从二级缓存移至一级缓存 ``` ### 详细步骤 #### 第1步:开始创建ServiceA ```java // 1. 实例化ServiceA(构造函数) ServiceA serviceA = new ServiceA(); // 2. 将ServiceA的工厂放入三级缓存 singletonFactories.put("serviceA", () -> getEarlyBeanReference("serviceA", serviceA)); // 3. 开始属性注入,发现需要ServiceB ``` #### 第2步:开始创建ServiceB ```java // 1. 实例化ServiceB(构造函数) ServiceB serviceB = new ServiceB(); // 2. 将ServiceB的工厂放入三级缓存 singletonFactories.put("serviceB", () -> getEarlyBeanReference("serviceB", serviceB)); // 3. 开始属性注入,发现需要ServiceA ``` #### 第3步:获取ServiceA早期引用 ```java // 调用getSingleton("serviceA", true) Object serviceA = singletonObjects.get("serviceA"); // 一级缓存:null if (serviceA == null) { serviceA = earlySingletonObjects.get("serviceA"); // 二级缓存:null if (serviceA == null) { ObjectFactory factory = singletonFactories.get("serviceA"); // 三级缓存:找到! serviceA = factory.getObject(); // 获取早期引用 earlySingletonObjects.put("serviceA", serviceA); // 移到二级缓存 singletonFactories.remove("serviceA"); // 从三级缓存移除 } } ``` #### 第4步:完成ServiceB创建 ```java // ServiceB获得ServiceA的早期引用,完成属性注入 serviceB.setServiceA(serviceA); // ServiceB初始化完成,放入一级缓存 singletonObjects.put("serviceB", serviceB); ``` #### 第5步:完成ServiceA创建 ```java // ServiceA获得ServiceB的完整引用,完成属性注入 serviceA.setServiceB(serviceB); // ServiceA初始化完成,从二级缓存移到一级缓存 singletonObjects.put("serviceA", serviceA); earlySingletonObjects.remove("serviceA"); ``` --- ## 💡 核心源码分析 ### getSingleton方法(核心逻辑) ```java /** * 从缓存中获取单例Bean * @param beanName Bean名称 * @param allowEarlyReference 是否允许早期引用 */ protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 一级缓存获取完整Bean Object singletonObject = this.singletonObjects.get(beanName); // 如果一级缓存没有,且Bean正在创建中 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // 二级缓存获取早期Bean singletonObject = this.earlySingletonObjects.get(beanName); // 如果二级缓存也没有,且允许早期引用 if (singletonObject == null && allowEarlyReference) { // 三级缓存获取Bean工厂 ObjectFactory singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // 通过工厂创建早期引用 singletonObject = singletonFactory.getObject(); // 升级到二级缓存 this.earlySingletonObjects.put(beanName, singletonObject); // 从三级缓存移除 this.singletonFactories.remove(beanName); } } } } return singletonObject; } ``` ### addSingletonFactory方法 ```java /** * 添加单例工厂到三级缓存 */ protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) { synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { // 放入三级缓存 this.singletonFactories.put(beanName, singletonFactory); // 从二级缓存移除 this.earlySingletonObjects.remove(beanName); } } } ``` --- ## 🤔 常见问题 ### Q1: 为什么需要三级缓存?二级不够吗? **答:** 三级缓存主要是为了支持AOP代理: - 如果Bean需要被代理(如@Transactional),不能直接暴露原始对象 - 通过ObjectFactory可以在需要时创建代理对象 - 保证循环依赖中获取的是正确的代理对象 ### Q2: 构造器循环依赖能解决吗? **答:** 不能!三级缓存只能解决属性注入的循环依赖: ```java // ❌ 构造器循环依赖 - 无法解决 @Component public class ServiceA { public ServiceA(ServiceB serviceB) { } } // ✅ 属性注入循环依赖 - 可以解决 @Component public class ServiceA { @Autowired private ServiceB serviceB; } ``` ### Q3: Prototype作用域的循环依赖呢? **答:** 不能解决!因为: - Prototype每次都创建新实例 - 没有缓存机制 - 会导致无限递归创建 --- ## 🎯 实践建议 ### 1. 避免循环依赖的最佳实践 ```java // ❌ 避免:直接的双向依赖 @Service public class UserService { @Autowired private OrderService orderService; } @Service public class OrderService { @Autowired private UserService userService; } // ✅ 推荐:引入第三方服务 @Service public class UserOrderService { @Autowired private UserService userService; @Autowired private OrderService orderService; public void processUserOrder(Long userId, Long orderId) { // 协调两个服务的交互 } } ``` ### 2. 使用@Lazy注解延迟加载 ```java @Service public class ServiceA { @Autowired @Lazy // 延迟加载,避免循环依赖 private ServiceB serviceB; } ``` ### 3. 通过ApplicationContext获取Bean ```java @Service public class ServiceA implements ApplicationContextAware { private ApplicationContext applicationContext; public void someMethod() { // 运行时获取,避免循环依赖 ServiceB serviceB = applicationContext.getBean(ServiceB.class); } } ``` --- ## 📝 学习总结 ### 关键点记忆 1. **三级缓存目的**:解决单例Bean的循环依赖 2. **核心思想**:提前暴露Bean的早期引用 3. **适用范围**:仅限于属性注入的循环依赖 4. **AOP支持**:通过ObjectFactory支持代理对象 ### 面试重点 - 能够清晰描述三级缓存的结构和作用 - 能够完整说出循环依赖的解决流程 - 了解哪些情况下循环依赖无法解决 - 知道避免循环依赖的最佳实践 --- ## 📚 扩展阅读 - Spring官方文档:Bean的生命周期 - 源码位置:`DefaultSingletonBeanRegistry`类 - 相关概念:Spring AOP、Bean作用域 - 设计模式:工厂模式、单例模式 --- **创建时间**:2025-06-25 **最后更新**:2025-06-25 **难度等级**:⭐⭐⭐⭐☆