notes/Spring三级缓存机制学习笔记.md
2025-06-25 19:21:02 +08:00

327 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 二级缓存早期暴露的Bean对象 */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** 三级缓存Bean工厂对象 */
private final Map<String, ObjectFactory<?>> 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
**难度等级**:⭐⭐⭐⭐☆