327 lines
8.6 KiB
Markdown
327 lines
8.6 KiB
Markdown
# 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
|
||
**难度等级**:⭐⭐⭐⭐☆ |