Gordon Bean
Gordon Bean

Reputation: 4602

Java Spring - enforce no bean inheritance

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.

Upvotes: 0

Views: 469

Answers (2)

Gordon Bean
Gordon Bean

Reputation: 4602

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.

Upvotes: 0

Andreas
Andreas

Reputation: 159086

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.

Upvotes: 2

Related Questions