写点什么

【Spring 三级缓存解密】如何优雅解决循环依赖难题

  • 2025-07-21
    福建
  • 本文字数:1610 字

    阅读完需:约 5 分钟

引言


在 Spring 框架的日常开发中,循环依赖问题如同一个幽灵,时不时困扰着开发者。当 Bean A 依赖 Bean B,而 Bean B 又依赖 Bean A 时,传统的创建流程会陷入死锁。本文将深入剖析 Spring 如何通过三级缓存机制破解这一难题,揭示其背后的设计智慧。


一、循环依赖的本质问题


循环依赖的根源在于对象创建的顺序性矛盾

@Componentpublic class ServiceA {    @Autowired    private ServiceB serviceB; // 需要ServiceB实例}
@Componentpublic class ServiceB { @Autowired private ServiceA serviceA; // 需要ServiceA实例}
复制代码


这种"鸡生蛋还是蛋生鸡"的问题,传统创建流程无法解决。


二、三级缓存机制全景解析


Spring 通过三级缓存架构破解循环依赖:



各级缓存的核心职责



三、破解循环依赖的全流程


以经典的 A→B→A 依赖链为例:



关键步骤解析


1、三级缓存注册(步骤 2/5):

// 实例化后立即注册addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, bean));
复制代码


2、早期引用生成(步骤 9-11):

protected Object getEarlyBeanReference(String beanName, Object bean) {    for (BeanPostProcessor bp : getBeanPostProcessors()) {        if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {            // 动态决策是否创建代理            bean = ((SmartInstantiationAwareBeanPostProcessor) bp)                       .getEarlyBeanReference(bean, beanName);        }    }    return bean;}
复制代码


3、缓存状态转移(步骤 12/16/20):

  • 被依赖后从三级缓存删除

  • 初始化完成后从二级缓存删除

  • 最终成品存于一级缓存


四、三级缓存的设计精妙之处


1. 双重延迟决策机制


public Object getEarlyBeanReference() {    // 延迟点1:只在被依赖时触发    // 延迟点2:动态决定是否创建代理    return (needsProxy ? createProxy(bean) : bean); }
复制代码


优势:避免为不需要代理或未发生循环依赖的 Bean 创建额外对象


2. 状态完整性保障



当创建代理时,Bean 已通过populateBean()完成属性注入,避免 NPE 风险


3. 对象版本统一性


// 最终代理一致性保证public void initializeBean() {    if (earlyProxyReference != null) {        return earlyProxyReference; // 复用已创建的代理    }    return createProxy(bean); // 无循环依赖时创建}
复制代码


4. 资源高效利用



五、疑难场景解决方案


1. 代理对象循环依赖


@Servicepublic class UserService {    @Autowired     private OrderService orderService;        @Transactional // 需要代理    public void createUser() {...}}
复制代码


解决方案

  • getEarlyBeanReference()中创建代理

  • 保证代理对象基于完成属性注入的状态


2. 多级循环依赖


A→B→C→A 依赖链:



处理流程

  1. C 获取 A 时触发三级缓存

  2. 返回 A 的早期引用

  3. C 完成初始化

  4. B 获得 C 的引用

  5. A 最终获得 B 的引用


3. 无法解决的场景



六、性能优化建议


1、避免循环依赖:重构设计,引入事件机制

// 使用事件解耦applicationContext.publishEvent(new UserCreatedEvent(user));
复制代码


2、懒加载优化

@Lazy@Autowiredprivate HeavyService heavyService; // 延迟初始化
复制代码


3、作用域控制

@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)public class RequestScopedBean {...}
复制代码


结论


Spring 的三级缓存机制通过以下创新设计解决循环依赖:

  1. 空间换时间:通过三级缓存状态管理打破创建顺序限制

  2. 延迟决策:在被依赖时才决定是否创建代理

  3. 状态保障:确保代理对象基于完整初始化状态

  4. 资源优化:避免不必要的对象创建


理解三级缓存不仅帮助解决循环依赖异常,更是深入掌握 Spring 框架设计思想的钥匙。正如 Spring 框架创始人 Rod Johnson 所说:"好的框架设计是在约束与灵活性之间找到完美平衡",三级缓存正是这种平衡的艺术体现。


文章转载自:佛祖让我来巡山

原文链接:https://www.cnblogs.com/sun-10387834/p/18992795

体验地址:http://www.jnpfsoft.com/?from=001YH

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
【Spring三级缓存解密】如何优雅解决循环依赖难题_spring_不在线第一只蜗牛_InfoQ写作社区