I have following configuration:
@Qualifier1
@Qualifier2
@Bean
public MyBean bean1(){...}
@Qualifier2
@Qualifier3
@Bean
public MyBean bean2(){...}
@Qualifier1
@Qualifier2
@Qualifier3
@Bean
public MyBean bean3(){...}
@Qualifier3
@Bean
public MyBean bean4(){...}
@Qualifier1
@Bean
public MyBean bean5(){...}
And it is the injection place:
@Qualifier2
@Qualifier3
@Autowired:
private List<MyBean> beans;
By default spring uses AND
logic for each @Qualifier
So bean2
and bean3
will be injected.
But I want to have OR
logic for that stuff so I expect beans bean1
bean2
bean3
and bean4
to be injected
How can I achieve it?
@Qualifier
annotation is not repeatable so I have to create meta annotation for each annotation:
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Qualifier1 {
}
We can do that via the @Qualifier annotation. For instance, we could specify that we want to use the bean returned by the johnEmployee method by using the @Qualifier annotation.
What is @Qualifier in Spring Boot? @Qualifier is an annotation used to specify which Spring managed bean should be injected via @Autowired. If you were to run the example without @Qualifier, the following exception would be thrown... This error says it all.
The @Qualifier annotation can be used on any class annotated with @Component or on methods annotated with @Bean . This annotation can also be applied on constructor arguments or method parameters. Injecting Bike bean in VehicleService using @Autowired with @Qualifier annotation.
The difference are that @Autowired and @Qualifier are the spring annotation while @Resource is the standard java annotation (from JSR-250) . Besides , @Resource only supports for fields and setter injection while @Autowired supports fields , setter ,constructors and multi-argument methods injection.
What if you used marker interfaces instead of qualifiers? For example:
public class MyBean1 extends MyBean implements Marker1 {}
public class MyBean2 extends MyBean implements Marker2 {}
public class MyBean12 extends MyBean implements Marker1, Marker2 {}
Then using this:
@Bean
public MyBean1 myBean1() {
//...
}
@Bean
public MyBean2 myBean2() {
//...
}
@Bean
public MyBean12 myBean12() {
//...
}
and this:
@Autowired private List<Marker1> myBeans;
You would get a list of myBean1
and myBean12
beans.
And for this:
@Autowired private List<Marker2> myBeans;
You would get a list of myBean2
and myBean12
beans.
Will this work?
UPDATE I
Custom FactoryBean
I implemented TagsFactoryBean class and @Tags annotation which you can use to solve your task (I hope :)).
First, mark your beans with @Tags
annotation:
@Tags({"greeting", "2letters"})
@Bean
public Supplier<String> hi() {
return () -> "hi";
}
@Tags({"parting", "2letters"})
@Bean
public Supplier<String> by() {
return () -> "by";
}
@Tags("greeting")
@Bean
public Supplier<String> hello() {
return () -> "hello";
}
@Tags("parting")
@Bean
public Supplier<String> goodbye() {
return () -> "goodbye";
}
@Tags("other")
@Bean
public Supplier<String> other() {
return () -> "other";
}
Then prepare TagsFactoryBean
:
@Bean
public TagsFactoryBean words() {
return TagsFactoryBean.<Supplier>builder()
.tags("greeting", "other")
.type(Supplier.class)
.generics(String.class)
.build();
}
Here tags
is an array of desired tags whose beans should be selected, type
is a selected beans type, and generics
is an array of generic types of the beans. The last parameter is optional and should be used only if your beans are generic.
Then you can use it with @Qualifier
annotation (otherwise Spring injects all beans of Supplier<String>
type):
@Autowired
@Qualifier("words")
private Map<String, Supplier<String>> beans;
The Map beans
will contain three beans: hi
, hello
and other
(their name are keys of the Map and their instances are its values).
More usage examples you can find in tests.
UPDATE II
Custom AutowireCandidateResolver
Thanks to @bhosleviraj recommendation, I implemented TaggedAutowireCandidateResolver that simplifies the process of autowiring the desired beans. Just mark your beans and the autowired collection with the same tags and you will get them injected into the collection:
@Autowired
@Tags({"greeting", "other"})
private Map<String, Supplier<String>> greetingOrOther;
@Configuration
static class Beans {
@Tags({"greeting", "2symbols", "even"})
@Bean
public Supplier<String> hi() {
return () -> "hi";
}
@Tags({"parting", "2symbols", "even"})
@Bean
public Supplier<String> by() {
return () -> "by";
}
@Tags({"greeting", "5symbols", "odd"})
@Bean
public Supplier<String> hello() {
return () -> "hello";
}
@Tags({"parting", "7symbols", "odd"})
@Bean
public Supplier<String> goodbye() {
return () -> "goodbye";
}
@Tags({"other", "5symbols", "odd"})
@Bean
public Supplier<String> other() {
return () -> "other";
}
}
You can use not only the Map for injecting beans but also other Collections.
To make it work you have to register a CustomAutowireConfigurer
bean in your application and provide it with TaggedAutowireCandidateResolver
:
@Configuration
public class AutowireConfig {
@Bean
public CustomAutowireConfigurer autowireConfigurer(DefaultListableBeanFactory beanFactory) {
CustomAutowireConfigurer configurer = new CustomAutowireConfigurer();
beanFactory.setAutowireCandidateResolver(new TaggedAutowireCandidateResolver());
configurer.postProcessBeanFactory(beanFactory);
return configurer;
}
}
More usage examples see in this Test.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With