Spring循环依赖解决方案深度解析:三级缓存的设计艺术!

难度:⭐⭐⭐⭐⭐ | 适合人群:想深入理解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依赖BB依赖A  →  循环依赖

A依赖BB依赖C,C依赖A  →  循环依赖

场景演示

场景1:双向依赖

@Service
public class A {
    @Autowired
    private B b;  // A依赖B
}

@Service
public class B {
    @Autowired
    private A a;  // B依赖A
}

依赖关系图:

   ABA  ← 形成循环

场景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
}

依赖关系图:

   AB
   ↑   ↓
   ←─ 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,已实例化但未完全初始化
三级缓存singletonFactoriesBean工厂用于创建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放入一级缓存 ✅
│       └─ 返回BA ✅
├─ A的属性填充完成
├─ A初始化完成
└─ A放入一级缓存 ✅

完成!AB都创建成功!

详细步骤解析

阿西噶阿西: "我们一步步来看。"

步骤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);
    }
}

🔧 第七问:手写简易版三级缓存

目标

实现一个最简单的三级缓存机制,支持:

  1. ✅ 解决循环依赖
  2. ✅ 模拟三级缓存
  3. ✅ 模拟提前暴露

实现代码

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

三级缓存

  1. 一级缓存(singletonObjects)- 完整Bean
  2. 二级缓存(earlySingletonObjects)- 早期引用
  3. 三级缓存(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循环依赖的解决方案!

如果这篇文章对你有帮助,请:

  • 👍 点赞支持
  • ⭐ 收藏备用
  • 🔄 转发分享
  • 💬 评论交流

感谢阅读,期待下次再见! 👋