【问题标题】:Why does using @Autowired not lead to a circular dependency, but autowiring the constructor does?为什么使用 @Autowired 不会导致循环依赖,但自动装配构造函数会导致?
【发布时间】:2020-05-22 18:37:53
【问题描述】:

我注意到我正在开发的应用程序中存在一些奇怪的错误。在修改一个类时,我移动了一堆属性以在构造函数中自动装配,而不是使用字段注入,但是,由于循环依赖,这导致我在启动时出错。以下是导致错误的依赖项的细分:

  • 我正在尝试在TargetClass 中使用ServiceA
  • ServiceA 通过其构造函数注入了 ServiceB
  • ServiceBServiceC 通过现场注入注入
  • ServiceCTargetClass 通过现场注入注入

我正在研究重构它的方法,或者尝试将一些逻辑移动到更好的地方,因此我删除了这种循环依赖,但是,我很好奇为什么注入 ServiceA 的两种不同方法会导致两种不同结果:

如果我通过字段注入注入ServiceA,只需在字段上使用@Autowired,我的应用程序就可以正常启动。但是,如果我将TargetClass 切换为使用构造函数注入,则会收到有关循环依赖的错误。

Spring 处理两种类型的注入的方式有什么不同,导致一种在这种情况下失败,而另一种则正常工作?

【问题讨论】:

  • 通过使用@Autowired 字段注入,您的依赖项会在需要时加载,而不是在加载上下文时加载。使用构造函数注入,spring 无法决定首先创建哪个服务。检查这个:baeldung.com/circular-dependencies-in-spring
  • 我想我明白这是怎么发生的。如果这也是您的想法,请告诉我:当我不使用构造函数注入创建它时,第一次需要它时,它不需要ServiceA,因此它在没有ServiceA 的情况下创建TargetClass。稍后,当需要ServiceA 时,它会被创建,我们使用已创建的现有TargetClass。当我们进行构造函数注入时,这个链就会被绊倒,因为它现在必须立即创建它们,而不是等到有需要时才创建它们?

标签: spring spring-boot dependency-injection


【解决方案1】:

字段注入的工作原理类似于

ServiceA serviceA = new ServiceA();
ServiceB serviceB = new ServiceB();
serviceA.serviceB = serviceB;
serviceB.serviceA = serviceA;

构造函数注入失败,因为

ServiceA serviceA = new ServiceA(????); // Cannot inject serviceB because to create serviceB I need serviceA which is being constructed

不过,即使使用构造函数,也有办法实现循环依赖。

class ServiceA {
  javax.inject.Provider<ServiceB> serviceBProvider;

  @Autowired ServiceA(javax.inject.Provider<ServiceB> serviceBProvider) {
     this.serviceBProvider = serviceBProvider;
  }

   void later() {
     this.serviceBProvider.get().methodOfServiceB();
   }

   void methodOfServiceA() {}
}

class ServiceB {
  javax.inject.Provider<ServiceA> serviceAProvider;

  @Autowired ServiceB(javax.inject.Provider<ServiceA> serviceAProvider) {
     this.serviceAProvider = serviceAProvider;
  }

   void later() {
     this.serviceAProvider.get().methodOfServiceA();
   }

   void methodOfServiceB() {}
}

它之所以有效,是因为 Spring 确实是这样的

Provider<ServiceA> serviceAProvider = new Provider<>();
Provider<ServiceB> serviceBProvider = new Provider<>();
ServiceA serviceA = new ServiceA(serviceBProvider);
ServiceB serviceB = new ServiceB(serviceAProvider);
serviceAProvider.set(serviceA);
serviceBProvider.set(serviceB);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-30
    • 1970-01-01
    • 2017-10-19
    • 1970-01-01
    • 2012-11-13
    相关资源
    最近更新 更多