Reputation: 5091
Spring's dependency injection allows distinguishing multiple implementations for the same interface with the @Qualifier("<flavor>")
annotation.
For example, we could have:
public class Consumer {
@Autowired("b") private SomeInterface dependency;
}
public interface SomeInterface { }
@Component @Qualifier("a")
public class VariantA implements SomeInterface { }
@Component @Qualifier("b")
public class VariantB implements SomeInterface { }
This requires that both, the consumer, and the implementations, use the same constant Strings to identify the variants, in this example "a" and "b". As a clean coder, my first impulse is to create constants for these Strings.
But where should I put them?
Putting them in the Consumer is out of the question, because that would invert the dependency in an illegal direction, requiring the implementations to know their consumers:
public class Consumer {
public final static String VARIANT_B = "b";
@Autowired(Consumer.VARIANT_B) private SomeInterface dependency;
}
@Component @Qualifier(Consumer.VARIANT_B)
public class VariantB implements SomeInterface { }
Putting them in the implementations seems also to be besides the point, reintroducing the consumer-knows-implementation dependency that dependency injection wants to remove in the first place:
public class Consumer {
@Autowired(VariantB.VARIANT_B) private SomeInterface dependency;
}
@Component @Qualifier(VariantB.VARIANT_B)
public class VariantB implements SomeInterface {
public final static String VARIANT_B = "b";
}
Putting them in the interface seems to be an option, but is only possible if the interface is mine and I am free to change it. Also, the notion of having the interface "know" all its implementation variants beforehand sounds a little strange.
public class Consumer {
@Autowired(SomeInterface.VARIANT_B) private SomeInterface dependency;
}
public interface SomeInterface {
public final static String VARIANT_A = "a";
public final static String VARIANT_B = "b";
}
@Component @Qualifier(SomeInterface.VARIANT_A)
public class VariantA implements SomeInterface { }
@Component @Qualifier(SomeInterface.VARIANT_B)
public class VariantB implements SomeInterface { }
The cleanest solution seems to be putting the constants into a separate development object. This looks a little overhead-y, however:
public class Consumer {
@Autowired(ImplementationVariant.B) private SomeInterface dependency;
}
public interface SomeInterface { }
public class ImplementationVariant {
public final static String A= "a";
public final static String B= "b";
}
@Component @Qualifier(ImplementationVariant.A)
public class VariantA implements SomeInterface { }
@Component @Qualifier(ImplementationVariant.B)
public class VariantB implements SomeInterface { }
Upvotes: 5
Views: 1671
Reputation: 3881
See my answer here: Typesafe @Qualifier for Spring Boot to avoid errors with strings for details.
Create you own annotations like
@Qualifier("a")
@Retention(RetentionPolicy.RUNTIME)
public @interface VariantA {}
@Qualifier("b")
@Retention(RetentionPolicy.RUNTIME)
public @interface VariantB {}
Then use them like this:
@Autowired
@VariantB
private SomeInterface dependency;
@Component
@VariantA
public class VariantA implements SomeInterface { }
@Component
@VariantB
public class VariantB implements SomeInterface { }
Upvotes: 2
Reputation: 131496
I would choose E : the standard way.
Adding this indirection to identify beans
names adds boiler plate code, is not standard (your peers will be surprised) and is not robust either.
Why no robust ? A qualifier expects a string and not an enum value that is strongly bounded. It means that you could pass an incorrect string such as @Component @Qualifier(OtherImplementationVariant.B)
and it will still compile fine.
A more robust alternative to achieve that would be to favor the @Bean
method factory for beans that have and that use @Qualifier
and so to set dependencies between beans by invoking the @Bean
method. But there still, that is verbose and not standard at all to generalize them for such a requirement. So not a good idea either.
So I think that you should just stick to standards because if a mismatch occurs and that a dependency cannot be resolved, the issue will be detected as soon as the spring boot startup.
Upvotes: 0