开发者

Spring无法解决循环依赖的五种场景分析

开发者 https://www.devze.com 2025-05-27 10:20 出处:网络 作者: 雾缘枯
目录一、构造器注入引发的循环依赖1. 问题复现2. 原理分析3. 解决方案二、原型(Prototype)作用域的循环依赖1. 问题复现2. 原理分析3. 解决方案三、@Async 注解导致的代理冲突1. 问题复现2. 原理分析3. 解决方案四、
目录
  • 一、构造器注入引发的循环依赖
    • 1. 问题复现
    • 2. 原理分析
    • 3. 解决方案
  • 二、原型(Prototype)作用域的循环依赖
    • 1. 问题复现
    • 2. 原理分析
    • 3. 解决方案
  • 三、@Async 注解导致的代理冲突
    • 1. 问题复现
    • 2. 原理分析
    • 3. 解决方案
  • 四、Configuration 类之间的循环依赖
    • 1. 问题复现
    • 2. 原理分析
    • 3. 解决方案
  • 五、自定义 BeanPostProcessor 引发的冲突
    • 1. 问题复现
    • 2. 原理分析
    • 3. 解决方案
  • 六、终极解决方案工具箱 
    • 结语:跳出循环依赖的思维陷阱

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

      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 尚未放入三级缓存。
      • 生命周期冲突:

      Spring无法解决循环依赖的五种场景分析

      3. 解决方案

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

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

      1. 问题复现

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

      2. 原理分析

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

      • Spring 官方限制:明确说明js不处理原型 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 Asy编程客栈ncService { /* ... */ }
      • 方案 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. 原理分析

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

      • Spring 启动流程

      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 的严格分层

      以上就是Spring无法解决循环依赖的五种场景分析的详细内容,更多关于Spring无法解决循环依赖的资料请关注编程客栈(www.devze.com)编程客栈其它相关文章!

      0

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      关注公众号