难度:⭐⭐⭐⭐⭐ | 适合人群:想深入理解Spring核心原理的开发者
💥 开场:一次"诡异"的启动失败
时间: 周五下午5点
地点: 办公室(准备下班)
事件: 项目启动报错
我: "奇怪,怎么启动报错了?" 😰
错误信息:
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| userService defined in file [UserService.class]
↑ ↓
| orderService defined in file [OrderService.class]
└─────┘
我: "循环依赖?什么鬼?" 🤔
代码是这样的:
@Service
public class UserService {
@Autowired
private OrderService orderService; // UserService依赖OrderService
public UserService(OrderService orderService) { // 构造器注入
this.orderService = orderService;
}
public void createUser() {
System.out.println("创建用户");
}
}
@Service
public class OrderService {
@Autowired
private UserService userService; // OrderService依赖UserService
public OrderService(UserService userService) { // 构造器注入
this.userService = userService;
}
public void createOrder() {
System.out.println("创建订单");
}
}
我: "两个Service互相依赖,有问题吗?之前好像也这样写过啊..." 😕
哈吉米走过来: "你用构造器注入了吧?"
我: "对啊,网上说构造器注入更好。"
哈吉米: "构造器注入解决不了循环依赖,改成字段注入试试。"
我: "为什么?" 🤔
南北绿豆凑过来: "这就要从Spring的三级缓存说起了..."
阿西噶阿西也来了: "对对对,三级缓存是Spring解决循环依赖的精髓!我给你讲讲..."
🎯 第一问:什么是循环依赖?
定义
循环依赖: 两个或多个Bean互相依赖,形成闭环。
A依赖B,B依赖A → 循环依赖
A依赖B,B依赖C,C依赖A → 循环依赖
场景演示
场景1:双向依赖
@Service
public class A {
@Autowired
private B b; // A依赖B
}
@Service
public class B {
@Autowired
private A a; // B依赖A
}
依赖关系图:
A
↓
B
↓
A ← 形成循环
场景2:三方循环
@Service
public class A {
@Autowired
private B b; // A→B
}
@Service
public class B {
@Autowired
private C c; // B→C
}
@Service
public class C {
@Autowired
private A a; // C→A
}
依赖关系图:
A → B
↑ ↓
←─ C ← 形成循环
为什么会有循环依赖?
哈吉米: "循环依赖在业务中很常见。"
实际业务场景:
// 用户服务
@Service
public class UserService {
@Autowired
private OrderService orderService; // 查询用户的订单
public User getUserWithOrders(Long userId) {
User user = findById(userId);
List<Order> orders = orderService.findByUserId(userId);
user.setOrders(orders);
return user;
}
}
// 订单服务
@Service
public class OrderService {
@Autowired
private UserService userService; // 查询订单的用户信息
public Order getOrderWithUser(Long orderId) {
Order order = findById(orderId);
User user = userService.findById(order.getUserId());
order.setUser(user);
return order;
}
}
这就形成了循环依赖!
🔄 第二问:循环依赖的问题
问题演示
如果没有解决方案会怎样?
// 创建A
A a = new A();
↓
需要注入B
↓
创建B
↓
需要注入A
↓
创建A ← 又要创建A?
↓
需要注入B
↓
创建B ← 又要创建B?
↓
无限循环... 💥
结果: 栈溢出(StackOverflowError)
手写代码验证
南北绿豆: "我们用代码模拟一下没有解决方案的情况。"
public class CircularDependencyDemo {
static class A {
private B b;
public A() {
System.out.println("创建A");
this.b = new B(); // 创建B
}
}
static class B {
private A a;
public B() {
System.out.println("创建B");
this.a = new A(); // 创建A
}
}
public static void main(String[] args) {
new A(); // 启动创建
}
}
运行结果:
创建A
创建B
创建A
创建B
创建A
创建B
...
Exception in thread "main" java.lang.StackOverflowError
死循环! 💀
🏗️ 第三问:Spring如何解决循环依赖?
核心思想:提前暴露
阿西噶阿西: "Spring的解决思路很巧妙:提前暴露!"
什么是提前暴露?
正常创建流程:
1. 实例化Bean(new)
2. 属性填充(注入依赖)
3. 初始化Bean(init方法)
4. 放入容器
↓
Bean完全创建好了才能使用
提前暴露:
1. 实例化Bean(new) ← 这一步完成就暴露!
2. 属性填充(注入依赖)
3. 初始化Bean(init方法)
4. 放入容器
↓
虽然还没完全创建好,但可以先给别人用
三级缓存
哈吉米: "Spring用三级缓存实现提前暴露。"
源码位置: org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
public class DefaultSingletonBeanRegistry {
/** 一级缓存:单例对象缓存(成品) */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 二级缓存:早期单例对象缓存(半成品) */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
/** 三级缓存:单例工厂缓存(工厂) */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** 正在创建的Bean名称集合 */
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
}
三级缓存说明
| 缓存 | 名称 | 存储内容 | 说明 |
|---|---|---|---|
| 一级缓存 | singletonObjects | 完整的Bean实例 | 成品Bean,可以直接使用 |
| 二级缓存 | earlySingletonObjects | 早期的Bean引用 | 半成品Bean,已实例化但未完全初始化 |
| 三级缓存 | singletonFactories | Bean工厂 | 用于创建Bean的早期引用 |
南北绿豆: "你可以这样理解:"
一级缓存 = 成品仓库(完全组装好的汽车)
二级缓存 = 半成品仓库(已组装底盘的汽车)
三级缓存 = 生产线(可以生产半成品的工厂)
🔍 第四问:三级缓存解决循环依赖的完整流程
场景设定
@Service
public class A {
@Autowired
private B b;
}
@Service
public class B {
@Autowired
private A a;
}
完整流程图
步骤1:创建A
├─ 实例化A(new A())
├─ 将A的工厂放入三级缓存
├─ 填充A的属性
│ └─ 需要注入B
│ └─ 步骤2:创建B
│ ├─ 实例化B(new B())
│ ├─ 将B的工厂放入三级缓存
│ ├─ 填充B的属性
│ │ └─ 需要注入A
│ │ └─ 步骤3:获取A
│ │ ├─ 从一级缓存获取A → 没有
│ │ ├─ 从二级缓存获取A → 没有
│ │ ├─ 从三级缓存获取A的工厂 → 有!
│ │ ├─ 调用工厂创建A的早期引用
│ │ ├─ 放入二级缓存
│ │ └─ 返回A的早期引用给B ✅
│ ├─ B的属性填充完成
│ ├─ B初始化完成
│ └─ B放入一级缓存 ✅
│ └─ 返回B给A ✅
├─ A的属性填充完成
├─ A初始化完成
└─ A放入一级缓存 ✅
完成!A和B都创建成功!
详细步骤解析
阿西噶阿西: "我们一步步来看。"
步骤1:实例化A
// 1. 实例化A
A a = new A(); // 只是new,还没有注入依赖
// 2. 标记A正在创建
singletonsCurrentlyInCreation.add("a");
// 3. 将A的工厂放入三级缓存
singletonFactories.put("a", () -> getEarlyBeanReference("a", a));
// 此时三级缓存状态:
singletonFactories = {
"a" -> ObjectFactory(() -> a)
}
步骤2:填充A的属性,需要B
// 填充A的属性
populateBean(a);
↓
发现需要注入B
↓
调用 getBean("b")
步骤3:实例化B
// 1. 实例化B
B b = new B(); // 只是new,还没有注入依赖
// 2. 标记B正在创建
singletonsCurrentlyInCreation.add("b");
// 3. 将B的工厂放入三级缓存
singletonFactories.put("b", () -> getEarlyBeanReference("b", b));
// 此时三级缓存状态:
singletonFactories = {
"a" -> ObjectFactory(() -> a),
"b" -> ObjectFactory(() -> b)
}
步骤4:填充B的属性,需要A
// 填充B的属性
populateBean(b);
↓
发现需要注入A
↓
调用 getBean("a")
步骤5:获取A(关键!)
/**
* 获取Bean
*/
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 1. 从一级缓存获取
Object singletonObject = this.singletonObjects.get("a");
if (singletonObject == null) { // 没有
// 2. 检查是否正在创建
if (isSingletonCurrentlyInCreation("a")) { // 是的!
// 3. 从二级缓存获取
singletonObject = this.earlySingletonObjects.get("a");
if (singletonObject == null) { // 也没有
// 4. 从三级缓存获取工厂
ObjectFactory<?> singletonFactory =
this.singletonFactories.get("a");
if (singletonFactory != null) { // 有!
// 5. 调用工厂创建早期引用
singletonObject = singletonFactory.getObject();
// 6. 放入二级缓存
this.earlySingletonObjects.put("a", singletonObject);
// 7. 移除三级缓存
this.singletonFactories.remove("a");
}
}
}
}
return singletonObject; // 返回A的早期引用
}
此时缓存状态:
singletonObjects = {} // 一级缓存:空
earlySingletonObjects = { // 二级缓存:有A的早期引用
"a" -> A@123
}
singletonFactories = { // 三级缓存:只剩B的工厂
"b" -> ObjectFactory(() -> b)
}
步骤6:B拿到A的早期引用
// B继续填充属性
b.a = getSingleton("a"); // 拿到A的早期引用(A@123)
// B的属性填充完成
// B初始化完成
// B放入一级缓存
singletonObjects.put("b", b);
此时缓存状态:
singletonObjects = { // 一级缓存:有完整的B
"b" -> B@456
}
earlySingletonObjects = { // 二级缓存:有A的早期引用
"a" -> A@123
}
singletonFactories = {} // 三级缓存:空
步骤7:A拿到完整的B
// A继续填充属性
a.b = getBean("b"); // 从一级缓存拿到完整的B(B@456)
// A的属性填充完成
// A初始化完成
// A放入一级缓存
singletonObjects.put("a", a);
// 移除A的早期引用
earlySingletonObjects.remove("a");
最终缓存状态:
singletonObjects = { // 一级缓存:有完整的A和B
"a" -> A@123,
"b" -> B@456
}
earlySingletonObjects = {} // 二级缓存:空
singletonFactories = {} // 三级缓存:空
完成!循环依赖解决! ✨
🤔 第五问:为什么需要三级缓存?二级不够吗?
疑问
哈吉米: "你可能会想,既然最终用的是二级缓存,为什么还需要三级缓存?"
一级缓存:完整Bean
二级缓存:早期引用
↓
两级缓存不就够了吗?
答案:为了支持AOP代理
南北绿豆: "关键在于AOP代理!"
场景:
@Service
public class A {
@Autowired
private B b;
@Transactional // ← 需要AOP代理
public void doSomething() {
// ...
}
}
@Service
public class B {
@Autowired
private A a; // ← B需要的是A的代理对象,不是原始对象!
}
AOP代理的时机
正常情况(无循环依赖):
1. 实例化Bean
2. 属性填充
3. 初始化
4. BeanPostProcessor后置处理 ← AOP代理在这里创建
5. 放入容器
AOP代理在Bean完全初始化后才创建!
循环依赖情况:
创建A
├─ 实例化A(原始对象 A@123)
├─ 属性填充
│ └─ 需要B
│ └─ 创建B
│ ├─ 实例化B
│ ├─ 属性填充
│ │ └─ 需要A
│ │ └─ 此时A还没初始化完,AOP代理还没创建
│ │ 但B需要的应该是A的代理对象,不是原始对象!
│ │ ↓
│ │ 怎么办? 🤔
三级缓存的作用
阿西噶阿西: "三级缓存存的是ObjectFactory,可以在需要时提前创建代理!"
三级缓存中的ObjectFactory:
// 放入三级缓存时
singletonFactories.put("a", () -> getEarlyBeanReference("a", mbd, a));
getEarlyBeanReference()方法:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd,
Object bean) {
Object exposedObject = bean; // 原始对象
// 遍历所有BeanPostProcessor
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp =
(SmartInstantiationAwareBeanPostProcessor) bp;
// 调用getEarlyBeanReference
// AOP的BeanPostProcessor会在这里创建代理对象!
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject; // 可能返回代理对象
}
AOP的BeanPostProcessor
AbstractAutoProxyCreator的实现:
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
// 如果需要代理,创建代理对象
return wrapIfNecessary(bean, beanName, cacheKey);
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 检查是否需要代理
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(
bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
// 创建代理对象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors,
new SingletonTargetSource(bean));
return proxy; // 返回代理对象
}
return bean; // 返回原始对象
}
对比:二级 vs 三级
哈吉米: "我们对比一下两种方案。"
方案A:只用二级缓存(不支持AOP)
// 实例化A
A a = new A();
// 直接放入二级缓存(原始对象)
earlySingletonObjects.put("a", a);
// 创建B时,从二级缓存获取A
// B拿到的是A的原始对象(A@123),不是代理对象
// 问题:如果A需要AOP代理,B拿到的对象不对! ❌
方案B:使用三级缓存(支持AOP)
// 实例化A
A a = new A();
// 放入三级缓存(工厂)
singletonFactories.put("a", () -> {
// 检查是否需要AOP代理
if (needProxy(a)) {
return createProxy(a); // 返回代理对象
}
return a; // 返回原始对象
});
// 创建B时,从三级缓存获取工厂并调用
Object aReference = singletonFactories.get("a").getObject();
// B拿到的是A的代理对象(AProxy@789)✅
总结
南北绿豆: "总结一下为什么需要三级缓存:"
只用二级缓存:
- 只能存储原始对象
- 无法提前创建代理
- 不支持AOP
使用三级缓存:
- 存储ObjectFactory
- 需要时调用工厂方法
- 可以提前创建代理
- 完美支持AOP ✅
💻 第六问:源码深度分析
核心方法:doCreateBean()
位置: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
protected Object doCreateBean(String beanName, RootBeanDefinition mbd,
@Nullable Object[] args) throws BeanCreationException {
BeanWrapper instanceWrapper = null;
// 1. 实例化Bean
if (instanceWrapper == null) {
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));
}
Object exposedObject = bean;
try {
// 4. 属性填充(可能触发循环依赖)
populateBean(beanName, mbd, instanceWrapper);
// 5. 初始化Bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
// ...
}
// 6. 处理循环依赖
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
}
}
return exposedObject;
}
关键方法:addSingletonFactory()
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
// 如果一级缓存没有
if (!this.singletonObjects.containsKey(beanName)) {
// 放入三级缓存
this.singletonFactories.put(beanName, singletonFactory);
// 移除二级缓存
this.earlySingletonObjects.remove(beanName);
// 记录已注册的单例Bean名称
this.registeredSingletons.add(beanName);
}
}
}
关键方法:getSingleton()
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 1. 从一级缓存获取
Object singletonObject = this.singletonObjects.get(beanName);
// 2. 一级缓存没有 && 正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 3. 从二级缓存获取
singletonObject = this.earlySingletonObjects.get(beanName);
// 4. 二级缓存也没有 && 允许早期引用
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// 双重检查
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 5. 从三级缓存获取工厂
ObjectFactory<?> singletonFactory =
this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 6. 调用工厂方法
singletonObject = singletonFactory.getObject();
// 7. 放入二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 8. 移除三级缓存
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
关键方法:addSingleton()
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 1. 放入一级缓存
this.singletonObjects.put(beanName, singletonObject);
// 2. 移除三级缓存
this.singletonFactories.remove(beanName);
// 3. 移除二级缓存
this.earlySingletonObjects.remove(beanName);
// 4. 记录
this.registeredSingletons.add(beanName);
}
}
🔧 第七问:手写简易版三级缓存
目标
实现一个最简单的三级缓存机制,支持:
- ✅ 解决循环依赖
- ✅ 模拟三级缓存
- ✅ 模拟提前暴露
实现代码
package com.example.circular;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* 简易版三级缓存容器
*/
public class SimpleSpringContainer {
/** 一级缓存:完整的Bean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
/** 二级缓存:早期的Bean引用 */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
/** 三级缓存:Bean工厂 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();
/** 正在创建的Bean */
private final Set<String> singletonsCurrentlyInCreation = new HashSet<>();
/** Bean定义(类信息) */
private final Map<String, Class<?>> beanDefinitions = new HashMap<>();
/**
* 注册Bean定义
*/
public void registerBeanDefinition(String beanName, Class<?> beanClass) {
beanDefinitions.put(beanName, beanClass);
System.out.println("注册Bean: " + beanName + " -> " + beanClass.getSimpleName());
}
/**
* 获取Bean
*/
public Object getBean(String beanName) {
System.out.println("\n>>> 获取Bean: " + beanName);
// 1. 从缓存获取
Object singleton = getSingleton(beanName);
if (singleton != null) {
System.out.println("<<< 从缓存返回: " + beanName);
return singleton;
}
// 2. 创建Bean
return createBean(beanName);
}
/**
* 从三级缓存获取Bean
*/
private Object getSingleton(String beanName) {
// 1. 从一级缓存获取
Object singletonObject = singletonObjects.get(beanName);
if (singletonObject != null) {
System.out.println(" [一级缓存] 找到: " + beanName);
return singletonObject;
}
// 2. 从二级缓存获取
singletonObject = earlySingletonObjects.get(beanName);
if (singletonObject != null) {
System.out.println(" [二级缓存] 找到: " + beanName);
return singletonObject;
}
// 3. 从三级缓存获取
ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);
if (singletonFactory != null) {
System.out.println(" [三级缓存] 找到工厂,调用创建早期引用");
// 调用工厂方法
singletonObject = singletonFactory.getObject();
// 放入二级缓存
earlySingletonObjects.put(beanName, singletonObject);
// 移除三级缓存
singletonFactories.remove(beanName);
System.out.println(" [二级缓存] 已放入: " + beanName);
return singletonObject;
}
return null;
}
/**
* 创建Bean
*/
private Object createBean(String beanName) {
System.out.println(" 【开始创建】 " + beanName);
// 1. 标记正在创建
singletonsCurrentlyInCreation.add(beanName);
Class<?> beanClass = beanDefinitions.get(beanName);
try {
// 2. 实例化Bean
Object bean = beanClass.getDeclaredConstructor().newInstance();
System.out.println(" 【实例化】 " + beanName + " -> " + bean.getClass().getSimpleName() + "@" +
Integer.toHexString(bean.hashCode()));
// 3. 将Bean工厂放入三级缓存(提前暴露)
final Object finalBean = bean;
singletonFactories.put(beanName, () -> finalBean);
System.out.println(" [三级缓存] 已放入工厂: " + beanName);
// 4. 属性填充(依赖注入)
populateBean(beanName, bean);
// 5. 初始化Bean
System.out.println(" 【初始化】 " + beanName);
// 6. 放入一级缓存
singletonObjects.put(beanName, bean);
System.out.println(" [一级缓存] 已放入: " + beanName);
// 7. 移除二级和三级缓存
earlySingletonObjects.remove(beanName);
singletonFactories.remove(beanName);
// 8. 移除创建标记
singletonsCurrentlyInCreation.remove(beanName);
System.out.println(" 【创建完成】 " + beanName);
return bean;
} catch (Exception e) {
throw new RuntimeException("创建Bean失败: " + beanName, e);
}
}
/**
* 属性填充(依赖注入)
*/
private void populateBean(String beanName, Object bean) {
System.out.println(" 【属性填充】 " + beanName);
// 获取所有字段
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
// 简化处理:字段名作为Bean名称
String fieldName = field.getName();
// 检查是否有对应的Bean定义
if (beanDefinitions.containsKey(fieldName)) {
System.out.println(" 需要注入: " + fieldName);
// 获取依赖的Bean(可能触发循环依赖)
Object dependency = getBean(fieldName);
// 注入
field.setAccessible(true);
try {
field.set(bean, dependency);
System.out.println(" 注入成功: " + fieldName + " -> " +
dependency.getClass().getSimpleName() + "@" +
Integer.toHexString(dependency.hashCode()));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
/**
* 打印缓存状态
*/
public void printCacheStatus() {
System.out.println("\n===== 缓存状态 =====");
System.out.println("一级缓存: " + singletonObjects.keySet());
System.out.println("二级缓存: " + earlySingletonObjects.keySet());
System.out.println("三级缓存: " + singletonFactories.keySet());
System.out.println("===================\n");
}
/**
* Bean工厂接口
*/
@FunctionalInterface
interface ObjectFactory<T> {
T getObject();
}
}
测试类
package com.example.circular;
/**
* 测试类A
*/
class A {
private B b; // A依赖B
public B getB() {
return b;
}
}
/**
* 测试类B
*/
class B {
private A a; // B依赖A
public A getA() {
return a;
}
}
/**
* 测试
*/
public class CircularDependencyTest {
public static void main(String[] args) {
System.out.println("========== 测试三级缓存解决循环依赖 ==========\n");
// 1. 创建容器
SimpleSpringContainer container = new SimpleSpringContainer();
// 2. 注册Bean定义
container.registerBeanDefinition("a", A.class);
container.registerBeanDefinition("b", B.class);
container.printCacheStatus();
// 3. 获取Bean(触发循环依赖)
System.out.println("========== 开始获取Bean A ==========");
A a = (A) container.getBean("a");
container.printCacheStatus();
// 4. 验证
System.out.println("========== 验证结果 ==========");
System.out.println("A: " + a.getClass().getSimpleName() + "@" +
Integer.toHexString(a.hashCode()));
System.out.println("A.b: " + a.getB().getClass().getSimpleName() + "@" +
Integer.toHexString(a.getB().hashCode()));
System.out.println("A.b.a: " + a.getB().getA().getClass().getSimpleName() + "@" +
Integer.toHexString(a.getB().getA().hashCode()));
System.out.println("\n验证: A == A.b.a ? " + (a == a.getB().getA()));
System.out.println("\n========== 测试完成 ==========");
}
}
运行结果
========== 测试三级缓存解决循环依赖 ==========
注册Bean: a -> A
注册Bean: b -> B
===== 缓存状态 =====
一级缓存: []
二级缓存: []
三级缓存: []
===================
========== 开始获取Bean A ==========
>>> 获取Bean: a
【开始创建】 a
【实例化】 a -> A@1b6d3586
[三级缓存] 已放入工厂: a
【属性填充】 a
需要注入: b
>>> 获取Bean: b
【开始创建】 b
【实例化】 b -> B@4554617c
[三级缓存] 已放入工厂: b
【属性填充】 b
需要注入: a
>>> 获取Bean: a
[三级缓存] 找到工厂,调用创建早期引用
[二级缓存] 已放入: a
<<< 从缓存返回: a
注入成功: a -> A@1b6d3586
【初始化】 b
[一级缓存] 已放入: b
【创建完成】 b
<<< 从缓存返回: b
注入成功: b -> B@4554617c
【初始化】 a
[一级缓存] 已放入: a
【创建完成】 a
<<< 从缓存返回: a
===== 缓存状态 =====
一级缓存: [a, b]
二级缓存: []
三级缓存: []
===================
========== 验证结果 ==========
A: A@1b6d3586
A.b: B@4554617c
A.b.a: A@1b6d3586
验证: A == A.b.a ? true
========== 测试完成 ==========
完美!循环依赖解决成功! ✨
❌ 第八问:哪些情况解决不了?
情况1:构造器注入的循环依赖
阿西噶阿西: "这是最常见的无法解决的情况。"
@Service
public class A {
private final B b;
@Autowired // 构造器注入
public A(B b) {
this.b = b;
}
}
@Service
public class B {
private final A a;
@Autowired // 构造器注入
public B(A a) {
this.a = a;
}
}
为什么解决不了?
创建A
├─ 调用构造器 new A(B b)
│ └─ 需要参数B
│ └─ 创建B
│ ├─ 调用构造器 new B(A a)
│ │ └─ 需要参数A
│ │ └─ 创建A ← 又要创建A
│ │ └─ 无限循环!
关键: 构造器执行时需要所有参数,但此时Bean还没实例化,无法提前暴露!
错误信息:
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| a defined in file [A.class]
↑ ↓
| b defined in file [B.class]
└─────┘
Action:
Relying upon circular references is discouraged and they are prohibited by default.
Update your application to remove the dependency cycle between beans.
情况2:prototype作用域的循环依赖
@Service
@Scope("prototype") // 原型模式
public class A {
@Autowired
private B b;
}
@Service
@Scope("prototype") // 原型模式
public class B {
@Autowired
private A a;
}
为什么解决不了?
prototype作用域:
- 每次getBean()都创建新实例
- 不会放入缓存
- 无法提前暴露
- 无法解决循环依赖
错误信息:
Error creating bean with name 'a': Requested bean is currently in creation:
Is there an unresolvable circular reference?
情况3:@Async导致的问题
@Service
public class A {
@Autowired
private B b;
@Async // 异步方法(需要代理)
public void doSomething() {
// ...
}
}
@Service
public class B {
@Autowired
private A a;
}
可能出现的问题:
@Async需要创建代理对象
如果配置不当,可能导致:
- B注入的是A的原始对象
- 调用A的@Async方法时不生效
情况4:setter注入 + @DependsOn
@Service
@DependsOn("b") // 强制依赖B
public class A {
@Autowired
private B b;
}
@Service
@DependsOn("a") // 强制依赖A
public class B {
@Autowired
private A a;
}
为什么解决不了?
@DependsOn强制指定Bean的创建顺序:
- A必须在B之后创建
- B必须在A之后创建
- 矛盾!无法满足
🎯 第九问:最佳实践与解决方案
最佳实践1:避免循环依赖(推荐)
哈吉米: "最好的方案是重新设计,避免循环依赖。"
方式1:提取公共服务
// 原来:A和B互相依赖
@Service
public class A {
@Autowired
private B b;
}
@Service
public class B {
@Autowired
private A a;
}
// 优化:提取公共服务C
@Service
public class A {
@Autowired
private C c; // A和B都依赖C
}
@Service
public class B {
@Autowired
private C c; // A和B都依赖C
}
@Service
public class C {
// 公共逻辑
}
方式2:使用事件机制
// 原来:UserService和OrderService互相依赖
@Service
public class UserService {
@Autowired
private OrderService orderService; // 依赖
public void createUser(User user) {
save(user);
orderService.createDefaultOrder(user); // 调用OrderService
}
}
// 优化:使用事件
@Service
public class UserService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void createUser(User user) {
save(user);
eventPublisher.publishEvent(new UserCreatedEvent(user)); // 发布事件
}
}
@Service
public class OrderService {
@EventListener // 监听事件
public void onUserCreated(UserCreatedEvent event) {
createDefaultOrder(event.getUser());
}
}
最佳实践2:构造器注入改字段注入
// ❌ 不行:构造器注入
@Service
public class A {
private final B b;
@Autowired
public A(B b) {
this.b = b;
}
}
// ✅ 可以:字段注入
@Service
public class A {
@Autowired
private B b;
}
// ✅ 也可以:setter注入
@Service
public class A {
private B b;
@Autowired
public void setB(B b) {
this.b = b;
}
}
最佳实践3:使用@Lazy延迟加载
// 方式1:在依赖上加@Lazy
@Service
public class A {
@Autowired
@Lazy // 延迟加载B
private B b;
}
@Service
public class B {
@Autowired
private A a;
}
// 方式2:在构造器参数上加@Lazy
@Service
public class A {
private final B b;
@Autowired
public A(@Lazy B b) { // 延迟加载B
this.b = b;
}
}
原理:
@Lazy会注入一个代理对象
↓
代理对象不需要立即创建真实对象
↓
打破了循环依赖
最佳实践4:使用ObjectProvider
@Service
public class A {
private final ObjectProvider<B> bProvider;
@Autowired
public A(ObjectProvider<B> bProvider) { // 注入Provider
this.bProvider = bProvider;
}
public void doSomething() {
B b = bProvider.getObject(); // 使用时再获取
// ...
}
}
最佳实践5:ApplicationContextAware
@Service
public class A implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void doSomething() {
B b = applicationContext.getBean(B.class); // 使用时再获取
// ...
}
}
💡 知识点总结
循环依赖核心要点
✅ 循环依赖定义
- 两个或多个Bean互相依赖
- 形成闭环
- 如果不处理会无限循环
✅ Spring的解决方案
- 三级缓存机制
- 提前暴露Bean
- 只能解决setter/字段注入的单例Bean
✅ 三级缓存
- 一级缓存(singletonObjects)- 完整Bean
- 二级缓存(earlySingletonObjects)- 早期引用
- 三级缓存(singletonFactories)- Bean工厂
✅ 为什么需要三级缓存?
- 支持AOP代理
- 提前创建代理对象
- 二级缓存不够
✅ 无法解决的情况
- 构造器注入的循环依赖
- prototype作用域的循环依赖
- @DependsOn导致的循环依赖
✅ 最佳实践
- 避免循环依赖(重新设计)
- 构造器改字段注入
- 使用@Lazy
- 使用ObjectProvider
- 使用事件机制
记忆口诀
循环依赖要提前暴露,
三级缓存来帮忙。
一级存放完整Bean,
二级保存早期引用。
三级缓存放工厂,
支持AOP创代理。
构造器注入解决不了,
字段注入才能搞定。
🤔 常见面试题
Q1: Spring如何解决循环依赖?
A:
Spring通过三级缓存解决循环依赖:
1. 一级缓存(singletonObjects):存放完整的Bean
2. 二级缓存(earlySingletonObjects):存放早期的Bean引用
3. 三级缓存(singletonFactories):存放Bean工厂
解决流程:
1. 创建A,实例化后放入三级缓存
2. 填充A的属性,需要B
3. 创建B,实例化后放入三级缓存
4. 填充B的属性,需要A
5. 从三级缓存获取A的工厂,创建A的早期引用
6. 早期引用放入二级缓存,返回给B
7. B创建完成,放入一级缓存
8. A拿到完整的B,继续创建
9. A创建完成,放入一级缓存
Q2: 为什么需要三级缓存?二级不够吗?
A:
需要三级缓存是为了支持AOP代理。
如果只用二级缓存:
- 只能存储原始对象
- 无法提前创建代理对象
- AOP不生效
使用三级缓存:
- 存储ObjectFactory
- 需要时调用工厂方法
- 可以提前创建代理对象
- 完美支持AOP
Q3: 构造器注入为什么解决不了循环依赖?
A:
构造器注入需要在实例化时就提供所有依赖:
创建A需要参数B → 创建B需要参数A → 死循环
而三级缓存的提前暴露是在实例化之后:
1. 实例化A(new A())
2. 放入三级缓存 ← 提前暴露在这里
3. 属性填充
构造器注入在步骤1就需要依赖,还没到步骤2,
所以无法提前暴露,无法解决循环依赖。
Q4: 如何避免循环依赖?
A:
最佳实践:
1. 重新设计,避免循环依赖(推荐)
2. 构造器注入改为字段注入
3. 使用@Lazy延迟加载
4. 使用ObjectProvider
5. 使用事件机制解耦
6. 提取公共服务
💬 写在最后
从循环依赖到三级缓存,我们深入剖析了Spring的精妙设计:
- 🔍 理解了循环依赖的本质
- 📊 掌握了三级缓存的完整流程
- 💻 手写了简易版实现
- 🎯 学习了最佳实践
这篇万字长文,希望能让你彻底搞懂Spring循环依赖的解决方案!
如果这篇文章对你有帮助,请:
- 👍 点赞支持
- ⭐ 收藏备用
- 🔄 转发分享
- 💬 评论交流
感谢阅读,期待下次再见! 👋