Java Spring – 强制不允许Bean继承

huangapple go评论76阅读模式
英文:

Java Spring - enforce no bean inheritance

问题

首先,一些代码:

interface Foo {
  void bar();
}

@Configuration
class FooConfig {
  @Bean
  public Foo foo() { return new MagicFoo(); }
  class MagicFoo implements Foo { ... }
}

@Component
class FooImpl implements Foo {
  public void bar() { }
}

@Component
class BarImpl {
  final Foo foo;
  public BarImpl(Foo foo) { this.foo = foo; }
}

在这个例子中,当实例化BarImpl时,Spring框架将查找与Foo匹配的bean,并优先选择由FooConfig定义的bean(即MagicFoo),而不是实现类FooImpl

我的问题是:是否有任何方法可以配置此代码,以便仅对与接口直接匹配的bean(例如Foo),而不是任何实现类(例如FooImpl)可用于特定接口依赖项?也就是说,是否可以通过某种方式配置Spring,以特殊的方式处理此类Foo依赖项。

在这样的配置中,如果我移除了FooConfig,尝试实例化BarImpl时会出现错误,因为找不到合适的bean,即使存在FooImpl的bean。换句话说,在这种期望的配置情况下,开发人员被迫明确地为已确定的接口定义bean,而不是将实现类标记为bean。

英文:

First, some code:

interface Foo {
  void bar();
}

@Configuration
class FooConfig {
  @Bean
  public Foo foo() { return new MagicFoo(); }
  class MagicFoo implements Foo { ... }
}

@Component
class FooImpl implements Foo {
  public void bar() { }
}

@Component
class BarImpl {
  final Foo foo;
  public BarImpl(Foo foo) { this.foo = foo; }
}

As this example stands, when BarImpl is instantiated, the Spring framework will look for a bean matching Foo and prefer the bean defined by FooConfig (i.e. the MagicFoo) over the implementing-class FooImpl.

My question is: is there any way to configure this code so that ONLY beans directly matching the interface (e.g. Foo), and not any implementing classes (e.g. FooImpl) are acceptable for a particular interface dependency? I.e. somehow configure Spring to treat Foo dependencies in this special way.

In such a configuration, if I removed FooConfig, I would get an error trying to instantiate BarImpl because no suitable bean can be found, even though a bean for FooImpl is available. In other words, with this desired configuration in place, the developer is forced to explicitly define beans for the identified interface instead of labeling implementing classes as beans.

答案1

得分: 2

如果您有多个实现了Foo接口的bean,它们都可以作为类型为Foo的参数进行自动装配的候选项。

您无法区分由@Bean创建的bean和由@Component创建的bean。

但是,您可以使用@Qualifier进行隔离/分离它们。

@Configuration
class FooConfig {
  @Bean
  @Qualifier("ABC")
  public Foo foo() { return new MagicFoo(); }
  class MagicFoo implements Foo { ... }
}

@Component
@Qualifier("XYZ")
class FooImpl implements Foo {
  public void bar() { }
}

@Component
class BarImpl {
  final Foo foo;
  public BarImpl(@Qualifier("ABC") Foo foo) { this.foo = foo; }
}

现在,参数只能由@Bean创建的bean进行自动装配,因为这是唯一具有正确限定符的bean。

英文:

If you have multiple beans implementing Foo, they are all candidates for autowiring a parameter of type Foo.

You cannot distinguish between beans created by @Bean vs beans created by @Component.

You can however use @Qualifier to isolate/separate them.

@Configuration
class FooConfig {
  @Bean
  @Qualifier("ABC")
  public Foo foo() { return new MagicFoo(); }
  class MagicFoo implements Foo { ... }
}

@Component
@Qualifier("XYZ")
class FooImpl implements Foo {
  public void bar() { }
}

@Component
class BarImpl {
  final Foo foo;
  public BarImpl(@Qualifier("ABC") Foo foo) { this.foo = foo; }
}

The parameter can now only be autowired by the @Bean created bean, because that is the only one with the correct qualifier.

答案2

得分: 0

在这一点上,我使用了以下设置来实现所需的行为:

  • 我为不应作为特殊接口的 bean 使用的实现创建了一个注解。(例如 @ActvityImplementation
    • 这些特殊接口还有一个关联的注解来识别它们。(例如 @ActivityInterface
  • 我有一个配置类,它引入了所有用 @ActivityImplementation 注解标记的 bean。
    • 参考链接:https://stackoverflow.com/a/48633041/2288986
  • 配置类会反射每个实现 bean,以提取带有 @ActivityInterface 注解的接口。
  • 对于每个实现 bean,我会查询 Spring 应用程序上下下文以查找与该接口匹配的 bean,并检查返回 bean 的类型。
    • 如果返回的 bean 与实现类型匹配(而不是预期的“magic”类型),我会抛出错误。

这个逻辑确保我(以及使用该框架的其他人)不会忘记配置“magic”接口实现。当“magic”实现存在时,它是返回的接口 bean。当“magic”实现不存在时,Spring 会回退到实现 bean,从而触发错误。

英文:

At this point I've accomplished the desired behavior with the following setup:

  • I created an annotation for the implementations that should not be used as beans for the special interfaces. (e.g. @ActvityImplementation)
    • The special interfaces also have an associated annotation that identifies them. (e.g. @ActivityInterface)
  • I have a configuration class that pulls in all the beans annotated with the @ActivityImplementation annotation.
  • The configuration class reflects on each implementation bean to extract the interface with the @ActivityInterface annotation.
  • For each implementation bean, I query the Spring application context for a bean matching the interface and check the type of the returned bean.
    • If the returned bean matches the implementation type (as opposed to the intended "magic" type) I throw an error.

This logic ensures that I (and anyone else using the framework) cannot forget to configure the "magic" interface implementation. When the "magic" implementation is present, it is the bean returned for the interface. When the "magic" implementation is not present, Spring falls back to the implementation bean, which then triggers the error.

huangapple
  • 本文由 发表于 2020年8月29日 01:16:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/63638288.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定