【问题标题】:Spring different beans for different consumers为不同的消费者提供不同的 bean
【发布时间】:2025-12-25 15:30:10
【问题描述】:

假设我有几个组件依赖于一项服务:

public interface MyService { ... }

// in package1
@Component
public class Package1Component1 {
  @Autowired
  private final MyService myService;
}

public class Package1Component2 {
  @Autowired
  private final MyService myService;
}

// in package 2
public class Package2Component1 {
  @Autowired
  private final MyService myService;
}

public class Package2Component2 {
  @Autowired
  private final MyService myService;
}

我有两个MyService 的实现:

@Service
public class MyServiceImpl1 implements MyService { ... }

@Service
public class MyServiceImpl2 implements MyService { ... }

我希望将 MyServiceImpl2 注入到 package2 中的所有组件和 MyServiceImpl1 其他任何地方

我不想使用 @Qualifier 来解决歧义,因为当您需要注入 MyService 时需要始终指定它,并且当我需要在任何地方切换到单个实现时(@ 987654328@ 是临时实现,只能在特定范围内使用)。

有没有办法为scope(java 包?)指定 bean,就像在 Angular 中我可以覆盖模块提供程序(在这种情况下为AuthService):

@NgModule({
  declarations: [LoginComponent, UserInfoComponent],
  providers: [
    {
      provide: AuthService,
      useClass: FacebookAuthService,
    },
  ],
})
export class AuthModule {}

【问题讨论】:

    标签: java spring dependency-injection


    【解决方案1】:

    您可以引入使用@Qualifier 注释的元注释并使用它。 准备好更改后,只需更改元注释上的限定符即可。

    【讨论】:

      【解决方案2】:

      我认为将 Angular 的特性与 Spring 关联起来并不正确,因为它们只是两个完全不同的基础架构,在所有方面。

      你为什么不想使用@Qualifier?是什么原因?因为,您描述的问题正是人们提出@Qualifier 实现的原因。

      我不想使用@Qualifier 来解决歧义,因为当您需要注入 MyService 时需要始终指定它,并且当我需要在任何地方切换到单个实现时更改大量文件。

      不是真的。您可以为您的 bean 定义提供 ID,并且不管您稍后将使用什么实现,具有相同 ID 的相同 bean 将被注入到任何您有资格注入的地方。您只会交换实现类。

      另外,Java 中的 package 不是 Bean 的作用域。包是用于对逻辑上相似的类进行分组的工具,它可以被视为一个范围,但对于类及其成员的可访问性/可见性,而不是对于 bean。

      Bean 作用域有不同的语义,你可以阅读它们here

      如果您要注入的类型有多个实现,则另一种方法是指定 bean 应符合候选资格。这是@Primary;然而,这个@Primary 将始终覆盖任何其他候选者,而使用@Qualifier,您可以利用更细粒度的控制来控制注入的位置。

      【讨论】:

      • 再一次关于限定符:它们打破了封装性和可组合性。作为组件的作者,我不应该考虑如何使用我的组件以及将哪些依赖项注入其中。使用限定符,作为组件的作者,我必须意识到某些依赖项有多种实现。但它打破了组件是独立事物的想法,它不应该知道它在哪个环境中使用。就像在 Angular 中一样,这就是我将 Angular DI 与 Spring DI 进行比较的原因。
      • 我不确定您的封装概念是否正确。封装只是隐藏一个实现级别并为该实现提供一个抽象。如果您有两个或更多符合条件的候选人,对于一种类型,您应该以某种方式区分它们。否则,您希望 Spring 根据 what 标准来决定注入哪一个?如果您使用继承的情况,您还可以使用特定的子类型而不是超类型声明字段。这也将消除歧义。