当前位置: 首页 > news >正文

Spring 无法解决循环依赖的 5 种场景

一、构造器注入引发的循环依赖

1. 问题复现

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

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

 报错信息:Requested bean is currently in creation: Is there an unresolvable circular reference?

2. 原理分析

  • 三级缓存失效:构造器注入要求在实例化阶段完成依赖注入,而此时 Bean 尚未放入三级缓存。

  • 生命周期冲突

3. 解决方案

  • 方案 1:将其中一个 Bean 改为 Setter / 字段注入
  • 方案 2:使用 `@Lazy` 延迟加载
    @Autowired
      public ServiceA(@Lazy ServiceB serviceB) { 
          this.serviceB = serviceB;
      }

二、原型(Prototype)作用域的循环依赖

1. 问题复现

@Scope("prototype")
@Component
public class PrototypeA {
    @Autowired private PrototypeB b;
}

@Scope("prototype")
@Component
public class PrototypeB {
    @Autowired private PrototypeA a;
}

2. 原理分析

  • 缓存机制不生效:原型 Bean 不会存入三级缓存,每次请求都创建新实例。

  • Spring 官方限制:明确说明不处理原型 Bean 的循环依赖。

3. 解决方案

  • 重构设计:避免原型 Bean 之间的循环依赖

  • 改用单例:评估是否真的需要原型作用域

三、@Async 注解导致的代理冲突

1. 问题复现

@Service
public class AsyncServiceA {
    @Autowired private AsyncServiceB serviceB;
    
    @Async
    public void asyncMethod() { /* ... */ }
}

@Service
public class AsyncServiceB {
    @Autowired private AsyncServiceA serviceA;
}

2. 原理分析

  • 代理时序问题@Async 通过后置处理器生成代理,可能破坏三级缓存机制。

  • 典型错误栈

    BeanCreationException: Error creating bean with name 'asyncServiceA': 
    Bean with name 'asyncServiceA' has been injected into other beans [...] in their raw version as part of a circular reference.

3. 解决方案

  • 方案 1:对异步方法所在类使用接口代理
    @Async
    public interface AsyncService {
        void asyncMethod();
    }
    
    @Service
    public class AsyncServiceImpl implements AsyncService { /* ... */ }
  • 方案 2:在注入点添加 @Lazy
    @Autowired @Lazy private AsyncServiceA serviceA;

四、Configuration 类之间的循环依赖

1. 问题复现

@Configuration
public class ConfigA {
    @Autowired private ConfigB configB;
}

@Configuration
public class ConfigB {
    @Autowired private ConfigA configA;
}

2. 原理分析

  • 配置类加载顺序:配置类需要优先初始化,无法通过常规循环依赖解决。

  • Spring 限制@Configuration 类被视为特殊 Bean,其代理机制与普通 Bean 不同。

3. 解决方案

  • 重构配置类:合并相关配置

  • 使用 @DependsOn:明确指定加载顺序

    @Configuration
    @DependsOn("configB")
    public class ConfigA { /* ... */ }

五、自定义 BeanPostProcessor 引发的冲突

1. 问题复现

@Component
public class CustomProcessor implements BeanPostProcessor {
    @Autowired private ServiceX x; // 依赖其他Bean
}

2. 原理分析

  • 处理器加载时序BeanPostProcessor 需要优先初始化,此时普通 Bean 尚未创建。

  • Spring 启动流程

 3. 解决方案

  • 避免在 BeanPostProcessor 中注入其他 Bean

  • 使用延迟注入

    private ObjectProvider<ServiceX> xProvider;
      
      public Object postProcessBeforeInitialization(Object bean, String beanName) {
          ServiceX x = xProvider.getIfAvailable();
          // ...
      }

六、终极解决方案工具箱 

问题类型应急方案根治方案
构造器循环依赖@Lazy 注解改为 Setter 注入
原型Bean循环依赖重构作用域引入中间类抽象依赖
AOP代理冲突接口代理模式调整切面作用顺序
配置类循环依赖@DependsOn 指定顺序合并配置类
BeanPostProcessor依赖ObjectProvider 延迟获取分离处理器与业务逻辑

结语:跳出循环依赖的思维陷阱

Spring 的循环依赖处理机制体现了框架设计的高度智慧,但作为开发者,最优雅的解决方案往往不是技术手段,而是架构设计。通过以下原则可从根本上避免循环依赖:

  1. 单一职责原则:拆分臃肿的 Bean

  2. 依赖倒置原则:面向接口编程

  3. 层次化设计:Controller -> Service -> Repository 的严格分层

相关文章:

  • 嵌入式 ARM Linux 系统构成(3):根文件系统(Root File System)
  • Magento2根据图片文件包导入产品图片
  • 解决火绒启动时,报安全服务异常,无法保障计算机安全
  • 工具介绍《netcat》
  • 电脑如何拦截端口号,实现阻断访问?
  • 物联网设备数据割裂难题:基于OAuth2.0的分布式用户画像系统设计!格行代理是不是套路?2025有什么比较好的副业?低成本的创业好项目有哪些?
  • 股票交易所官方api接口有哪些?获取和使用需要满足什么条件
  • segment-anything分割万物python环境部署和实现
  • Seata:分布式事务的终极解决方案
  • vue3中插槽
  • ElementUI 级联选择器el-cascader启用选择任意一级选项,选中后关闭下拉框
  • 微服务的认识与拆分
  • 9.1go结构体
  • 第9章 管理日志(网络安全防御实战--蓝军武器库)
  • Linux13-TCP\HTTP
  • 条码扫描手持pda,推动服装零售门店管理效率
  • 大模型开发(四):PET项目——新零售决策评价系统(上)
  • 大白话CSS 优先级计算规则的详细推导与示例
  • HarmonyOS NEXT开发实战:DevEco Studio中DeepSeek的使用
  • uniapp小程序对接腾讯IM即时通讯无ui集成(1)
  • 西安网站设计学校/开一个免费网站
  • 深圳做外贸网站/站长之家站长工具
  • 做书封面的网站/网址导航大全
  • 深圳专业网站建设/seo怎么优化排名
  • 为什么没人做同城购物网站/谷歌浏览器官网
  • 自己做网站项目/中国突然宣布一重磅消息