🔄Spring循环依赖:三级缓存的魔法!

31 阅读10分钟

副标题:为什么Spring可以解决循环依赖,而你的代码不行?🎯


🎬 开场:可怕的循环依赖

什么是循环依赖?

场景:A依赖BB依赖A

ServiceA {
    @Autowired
    private ServiceB serviceB;
}

ServiceB {
    @Autowired
    private ServiceA serviceA;
}

问题:谁先创建?

创建A → 需要注入B
      → 创建B → 需要注入A
              → 创建A → 需要注入B
                      → 创建B → 需要注入A
                              → ...
                              
无限循环!💥

真实案例

/**
 * 某项目的循环依赖噩梦
 */
@Service
public class OrderService {
    @Autowired
    private UserService userService;  // 需要用户服务
    
    public void createOrder(Long userId) {
        User user = userService.getUser(userId);
        // 创建订单...
    }
}

@Service
public class UserService {
    @Autowired
    private OrderService orderService;  // 需要订单服务
    
    public List<Order> getUserOrders(Long userId) {
        return orderService.getOrdersByUserId(userId);
    }
}

启动应用:
╔═══════════════════════════════════════╗
║  Error creating bean 'orderService'   ║
║  Circular dependency detected!        ║
╚═══════════════════════════════════════╝

程序员:???我明明用了@Autowired啊!为什么不行?

📚 循环依赖的三种情况

情况1:构造器循环依赖 ❌ Spring无法解决

/**
 * 构造器注入的循环依赖
 * Spring无法解决!
 */
@Service
public class ServiceA {
    
    private final ServiceB serviceB;
    
    // 构造器注入
    public ServiceA(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

@Service
public class ServiceB {
    
    private final ServiceA serviceA;
    
    // 构造器注入
    public ServiceB(ServiceA serviceA) {
        this.serviceA = serviceA;
    }
}

启动结果:
BeanCurrentlyInCreationException: 
Error creating bean with name 'serviceA': 
Requested bean is currently in creation

原因:
创建A → 调用构造器 → 需要完整的B
      → 创建B → 调用构造器 → 需要完整的A
              → 死锁!❌

情况2:Setter循环依赖 ✅ Spring可以解决

/**
 * Setter注入(字段注入)的循环依赖
 * Spring可以解决!
 */
@Service
public class ServiceA {
    
    @Autowired
    private ServiceB serviceB;  // 字段注入
}

@Service
public class ServiceB {
    
    @Autowired
    private ServiceA serviceA;  // 字段注入
}

启动结果:✅ 成功!

原因:
Spring的三级缓存机制可以解决!

情况3:Prototype循环依赖 ❌ Spring无法解决

/**
 * 原型模式的循环依赖
 * Spring无法解决!
 */
@Service
@Scope("prototype")  // 原型模式
public class ServiceA {
    
    @Autowired
    private ServiceB serviceB;
}

@Service
@Scope("prototype")  // 原型模式
public class ServiceB {
    
    @Autowired
    private ServiceA serviceA;
}

启动结果:❌ 报错

原因:
原型Bean每次都要创建新实例,
无法使用缓存,所以无法解决循环依赖

🎯 Spring的三级缓存

三级缓存是什么?

/**
 * Spring容器中的三级缓存
 * 
 * 源码位置:DefaultSingletonBeanRegistry
 */
public class DefaultSingletonBeanRegistry {
    
    /**
     * 一级缓存:singletonObjects
     * 
     * 存放:完全初始化好的Bean(成品)
     * 
     * 单例池,存放已经完全创建好的Bean
     */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    /**
     * 二级缓存:earlySingletonObjects
     * 
     * 存放:提前暴露的Bean(半成品)
     * 
     * 早期单例对象,存放实例化但未初始化完成的Bean
     */
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    
    /**
     * 三级缓存:singletonFactories
     * 
     * 存放:创建Bean的工厂(ObjectFactory)
     * 
     * 单例工厂,存放创建Bean的工厂对象
     */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
}

三级缓存的作用

一级缓存(singletonObjects):
- 存放完全初始化好的Bean
- 可以直接使用的Bean
- 最终的归宿

二级缓存(earlySingletonObjects):
- 存放提前暴露的Bean引用
- 解决循环依赖的关键
- 半成品Bean

三级缓存(singletonFactories):
- 存放创建Bean的工厂
- 支持AOP代理
- 延迟创建代理对象

为什么需要三级缓存?
- 一级:存储完整Bean
- 二级:存储半成品Bean,解决循环依赖
- 三级:支持AOP,延迟创建代理对象

🔍 循环依赖解决过程

完整流程图

A依赖BB依赖A的解决过程:

1. 创建A
   ├── 实例化A(调用构造器)
   ├── A对象创建完成(半成品)
   ├── 将AObjectFactory放入三级缓存
   ├── 填充属性:发现需要注入B
   └── 去获取B2. 创建B
          ├── 实例化B(调用构造器)
          ├── B对象创建完成(半成品)
          ├── 将BObjectFactory放入三级缓存
          ├── 填充属性:发现需要注入A
          └── 去获取A3. 获取A(从缓存)
                 ├── 一级缓存:没有
                 ├── 二级缓存:没有
                 ├── 三级缓存:有!取出AObjectFactory
                 ├── 调用ObjectFactory.getObject()得到A
                 ├── 将A放入二级缓存
                 ├── 删除三级缓存中的A
                 └── 返回A(半成品)
                     │
                     ↓
              4. B注入A(半成品)
                 ├── B的属性填充完成
                 ├── B初始化完成(@PostConstruct等)
                 ├── B是完整的Bean了
                 ├── 将B放入一级缓存
                 └── 返回B
                     │
                     ↓
       5. A注入B
          ├── A的属性填充完成
          ├── A初始化完成(@PostConstruct等)
          ├── A是完整的Bean了
          ├── 将A放入一级缓存
          └── 删除二级缓存中的A

完成!AB都初始化完成,并且互相注入成功!✅

关键源码

/**
 * 获取Bean的核心方法
 * 
 * 源码位置:AbstractBeanFactory.doGetBean()
 */
protected <T> T doGetBean(String name, ...) {
    
    // 1. 先从缓存获取(三级缓存查找)
    Object sharedInstance = getSingleton(name);
    
    if (sharedInstance != null) {
        // 缓存中有,直接返回
        return (T) sharedInstance;
    }
    
    // 2. 缓存中没有,开始创建Bean
    if (isSingletonCurrentlyInCreation(name)) {
        // 正在创建中,说明出现循环依赖
        // 从三级缓存获取
    }
    
    // 3. 创建Bean实例
    sharedInstance = createBean(name, ...);
    
    return (T) sharedInstance;
}

/**
 * 从三级缓存获取Bean
 * 
 * 源码位置:DefaultSingletonBeanRegistry.getSingleton()
 */
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    
    // 1. 从一级缓存获取(完整的Bean)
    Object singletonObject = this.singletonObjects.get(beanName);
    
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 正在创建中
        
        // 2. 从二级缓存获取(半成品Bean)
        singletonObject = this.earlySingletonObjects.get(beanName);
        
        if (singletonObject == null && allowEarlyReference) {
            
            synchronized (this.singletonObjects) {
                // 双重检查
                singletonObject = this.singletonObjects.get(beanName);
                
                if (singletonObject == null) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    
                    if (singletonObject == null) {
                        // 3. 从三级缓存获取(ObjectFactory)
                        ObjectFactory<?> singletonFactory = 
                            this.singletonFactories.get(beanName);
                        
                        if (singletonFactory != null) {
                            // 调用工厂方法获取Bean
                            singletonObject = singletonFactory.getObject();
                            
                            // 放入二级缓存
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            
                            // 删除三级缓存
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    
    return singletonObject;
}

/**
 * 创建Bean
 * 
 * 源码位置:AbstractAutowireCapableBeanFactory.doCreateBean()
 */
protected Object doCreateBean(String beanName, ...) {
    
    // 1. 实例化Bean(调用构造器)
    BeanWrapper instanceWrapper = createBeanInstance(beanName, ...);
    Object bean = instanceWrapper.getWrappedInstance();
    
    // 2. 将Bean的ObjectFactory放入三级缓存(提前暴露)
    if (isSingletonCurrentlyInCreation(beanName)) {
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, bean));
    }
    
    // 3. 填充属性(依赖注入)
    populateBean(beanName, bean, ...);
    
    // 4. 初始化Bean(InitializingBean、@PostConstruct等)
    bean = initializeBean(beanName, bean, ...);
    
    return bean;
}

💻 模拟实现

简化版三级缓存

/**
 * 简化版的Spring容器(模拟三级缓存)
 */
public class SimpleBeanFactory {
    
    // 一级缓存:完整的Bean
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
    
    // 二级缓存:半成品Bean
    private Map<String, Object> earlySingletonObjects = new HashMap<>();
    
    // 三级缓存:Bean工厂
    private Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();
    
    // 正在创建中的Bean
    private Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>());
    
    /**
     * 获取Bean
     */
    public Object getBean(String beanName) {
        // 1. 从缓存获取
        Object bean = getSingleton(beanName);
        if (bean != null) {
            return bean;
        }
        
        // 2. 标记为正在创建
        singletonsCurrentlyInCreation.add(beanName);
        
        // 3. 创建Bean
        bean = createBean(beanName);
        
        // 4. 放入一级缓存
        singletonObjects.put(beanName, bean);
        
        // 5. 删除二级和三级缓存
        earlySingletonObjects.remove(beanName);
        singletonFactories.remove(beanName);
        
        // 6. 标记创建完成
        singletonsCurrentlyInCreation.remove(beanName);
        
        return bean;
    }
    
    /**
     * 从三级缓存获取Bean
     */
    private Object getSingleton(String beanName) {
        // 一级缓存
        Object bean = singletonObjects.get(beanName);
        if (bean != null) {
            return bean;
        }
        
        // 如果正在创建中
        if (singletonsCurrentlyInCreation.contains(beanName)) {
            // 二级缓存
            bean = earlySingletonObjects.get(beanName);
            if (bean != null) {
                return bean;
            }
            
            // 三级缓存
            ObjectFactory<?> factory = singletonFactories.get(beanName);
            if (factory != null) {
                bean = factory.getObject();
                
                // 放入二级缓存
                earlySingletonObjects.put(beanName, bean);
                
                // 删除三级缓存
                singletonFactories.remove(beanName);
                
                return bean;
            }
        }
        
        return null;
    }
    
    /**
     * 创建Bean
     */
    private Object createBean(String beanName) {
        System.out.println("创建Bean: " + beanName);
        
        // 1. 实例化(调用构造器)
        Object bean = instantiate(beanName);
        
        // 2. 提前暴露(放入三级缓存)
        ObjectFactory<?> factory = () -> bean;
        singletonFactories.put(beanName, factory);
        
        // 3. 填充属性(依赖注入)
        populateBean(beanName, bean);
        
        // 4. 初始化
        initializeBean(beanName, bean);
        
        return bean;
    }
    
    /**
     * 实例化Bean
     */
    private Object instantiate(String beanName) {
        try {
            if ("serviceA".equals(beanName)) {
                return ServiceA.class.newInstance();
            } else if ("serviceB".equals(beanName)) {
                return ServiceB.class.newInstance();
            }
        } catch (Exception e) {
            throw new RuntimeException("实例化失败", e);
        }
        return null;
    }
    
    /**
     * 填充属性(依赖注入)
     */
    private void populateBean(String beanName, Object bean) {
        System.out.println("填充属性: " + beanName);
        
        if (bean instanceof ServiceA) {
            ServiceA serviceA = (ServiceA) bean;
            // 注入ServiceB
            serviceA.setServiceB((ServiceB) getBean("serviceB"));
            
        } else if (bean instanceof ServiceB) {
            ServiceB serviceB = (ServiceB) bean;
            // 注入ServiceA
            serviceB.setServiceA((ServiceA) getBean("serviceA"));
        }
    }
    
    /**
     * 初始化Bean
     */
    private void initializeBean(String beanName, Object bean) {
        System.out.println("初始化Bean: " + beanName);
        // @PostConstruct、InitializingBean等
    }
}

/**
 * 测试类
 */
public class ServiceA {
    private ServiceB serviceB;
    
    public void setServiceB(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

public class ServiceB {
    private ServiceA serviceA;
    
    public void setServiceA(ServiceA serviceA) {
        this.serviceA = serviceA;
    }
}

/**
 * 测试
 */
public class Test {
    public static void main(String[] args) {
        SimpleBeanFactory factory = new SimpleBeanFactory();
        
        ServiceA serviceA = (ServiceA) factory.getBean("serviceA");
        ServiceB serviceB = (ServiceB) factory.getBean("serviceB");
        
        System.out.println("ServiceA: " + serviceA);
        System.out.println("ServiceB: " + serviceB);
        System.out.println("成功解决循环依赖!");
    }
}

/**
 * 输出:
 * 创建Bean: serviceA
 * 填充属性: serviceA
 * 创建Bean: serviceB
 * 填充属性: serviceB
 * 初始化Bean: serviceB
 * 初始化Bean: serviceA
 * ServiceA: com.example.ServiceA@xxxxx
 * ServiceB: com.example.ServiceB@xxxxx
 * 成功解决循环依赖!
 */

🎨 循环依赖与AOP

为什么需要三级缓存?

问题:如果只有二级缓存,能解决循环依赖吗?

答案:可以解决,但无法支持AOP!

原因:

两级缓存方案:
一级缓存:完整Bean
二级缓存:半成品Bean(直接存对象)

问题:如果Bean需要AOP代理,什么时候创建代理?

方案1:实例化后立即创建代理
- 问题:所有Bean都要创建代理,浪费资源
- 很多Bean不需要代理

方案2:初始化后创建代理
- 问题:如果有循环依赖,注入的是原始对象,不是代理对象
- 导致AOP失效

三级缓存方案:
一级缓存:完整Bean
二级缓存:半成品Bean(可能是代理)
三级缓存:ObjectFactory(延迟创建代理)

优点:
- 只有在出现循环依赖时,才提前创建代理
- 没有循环依赖时,代理在初始化后创建
- 完美!✅

AOP + 循环依赖示例

/**
 * AOP + 循环依赖
 */
@Service
public class ServiceA {
    
    @Autowired
    private ServiceB serviceB;
    
    @Transactional  // 需要AOP代理
    public void methodA() {
        System.out.println("ServiceA.methodA()");
    }
}

@Service
public class ServiceB {
    
    @Autowired
    private ServiceA serviceA;
    
    public void methodB() {
        System.out.println("ServiceB.methodB()");
        serviceA.methodA();  // 调用A的方法
    }
}

/**
 * 创建流程:
 * 
 * 1. 创建ServiceA
 *    - 实例化ServiceA(原始对象)
 *    - 放入三级缓存:ObjectFactory(() -> getEarlyBeanReference(serviceA))
 *    - 填充属性:需要ServiceB
 *    
 * 2. 创建ServiceB
 *    - 实例化ServiceB(原始对象)
 *    - 放入三级缓存:ObjectFactory(() -> getEarlyBeanReference(serviceB))
 *    - 填充属性:需要ServiceA
 *    
 * 3. 从三级缓存获取ServiceA
 *    - 调用ObjectFactory.getObject()
 *    - 执行getEarlyBeanReference()
 *    - 检查ServiceA是否需要AOP代理
 *    - 需要!创建ServiceA的代理对象
 *    - 将代理对象放入二级缓存
 *    - 返回代理对象
 *    
 * 4. ServiceB注入ServiceA的代理对象 ✅
 * 
 * 5. ServiceB初始化完成
 * 
 * 6. ServiceA注入ServiceB
 * 
 * 7. ServiceA初始化
 *    - 不再创建代理(已经在步骤3创建了)
 *    
 * 结果:
 * - ServiceA是代理对象
 * - ServiceB持有的serviceA是代理对象
 * - AOP生效!✅
 */

关键源码

/**
 * 提前创建AOP代理的关键方法
 * 
 * 源码位置:AbstractAutoProxyCreator.getEarlyBeanReference()
 */
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    
    // 记录已经被提前代理的Bean
    this.earlyProxyReferences.put(cacheKey, bean);
    
    // 如果需要代理,创建代理对象
    return wrapIfNecessary(bean, beanName, cacheKey);
}

/**
 * 正常初始化后的AOP代理创建
 * 
 * 源码位置:AbstractAutoProxyCreator.postProcessAfterInitialization()
 */
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        
        // 如果已经提前创建了代理,不再创建
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

🎯 如何避免循环依赖

方法1:重构代码(推荐)⭐⭐⭐⭐⭐

/**
 * ❌ 不好的设计(循环依赖)
 */
@Service
public class OrderService {
    @Autowired
    private UserService userService;
}

@Service
public class UserService {
    @Autowired
    private OrderService orderService;
}

/**
 * ✅ 好的设计(消除循环依赖)
 * 
 * 方案1:抽取公共服务
 */
@Service
public class OrderService {
    @Autowired
    private UserService userService;
    
    @Autowired
    private OrderQueryService orderQueryService;  // 新服务
}

@Service
public class UserService {
    @Autowired
    private OrderQueryService orderQueryService;  // 注入查询服务
}

@Service
public class OrderQueryService {
    // 只负责查询,不依赖OrderService和UserService
}

/**
 * 方案2:使用事件驱动
 */
@Service
public class OrderService {
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    public void createOrder() {
        // 创建订单
        Order order = new Order();
        
        // 发布事件,而不是直接调用UserService
        eventPublisher.publishEvent(new OrderCreatedEvent(order));
    }
}

@Service
public class UserService {
    @EventListener
    public void onOrderCreated(OrderCreatedEvent event) {
        // 处理订单创建事件
    }
}

方法2:使用@Lazy ⭐⭐⭐⭐

/**
 * 使用@Lazy延迟注入
 */
@Service
public class ServiceA {
    
    private final ServiceB serviceB;
    
    // 构造器注入 + @Lazy
    public ServiceA(@Lazy ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

@Service
public class ServiceB {
    
    private final ServiceA serviceA;
    
    public ServiceB(@Lazy ServiceA serviceA) {
        this.serviceA = serviceA;
    }
}

/**
 * 原理:
 * @Lazy会创建一个代理对象
 * 只有在真正使用时才会去获取真实的Bean
 * 打破了循环依赖的链条
 */

方法3:使用Setter注入 ⭐⭐⭐

/**
 * ❌ 构造器注入(Spring无法解决)
 */
@Service
public class ServiceA {
    private final ServiceB serviceB;
    
    public ServiceA(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

/**
 * ✅ Setter注入(Spring可以解决)
 */
@Service
public class ServiceA {
    
    @Autowired
    private ServiceB serviceB;  // 字段注入(本质是setter)
}

🎉 总结

核心要点

1. 循环依赖的类型
   - 构造器循环依赖:❌ 无法解决
   - Setter循环依赖:✅ 可以解决
   - Prototype循环依赖:❌ 无法解决

2. 三级缓存
   - 一级:singletonObjects(完整Bean)
   - 二级:earlySingletonObjects(半成品Bean)
   - 三级:singletonFactories(Bean工厂)

3. 解决过程
   - 实例化Bean
   - 提前暴露(放入三级缓存)
   - 填充属性(依赖注入)
   - 从缓存获取依赖
   - 初始化完成

4. 为什么需要三级缓存?
   - 支持AOP代理
   - 延迟创建代理
   - 只在需要时创建

记忆口诀

循环依赖三级存,
单例对象和工厂。

一级缓存存成品,
完全初始化的Bean。
二级缓存存半成品,
提前暴露来救急。
三级缓存存工厂,
延迟创建AOP代理。

创建Bean先实例化,
ObjectFactory放三级。
填充属性要注入,
从缓存中去获取。

一级二级都没有,
三级工厂来救场。
调用工厂得对象,
放入二级删三级。

注入完成再初始化,
Bean完整放一级。
删除二级和三级,
循环依赖已解决!

构造器注入解决不了,
Setter注入没问题。
原型模式也不行,
单例模式才可以!

愿你的Bean没有循环,依赖永远清晰! 🔄✨