I run into a code that has some strange @Qualifier behavior. It is not what I would expect.
Let's consider the following code snippet:
@SpringBootApplication
class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
Foo foo1() {
return new Foo("foo1");
}
@Bean
Foo foo2() {
return new Foo("foo2");
}
static class Foo {
private final String name;
public Foo(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
@Component
static class Bar {
private Foo fooOne;
private Foo fooTwo;
@Autowired
@Qualifier("foo3")
void setFoo(Foo foo1, Foo foo2) {
this.fooOne = foo1;
this.fooTwo = foo2;
}
@PostConstruct
void printFoo() {
System.out.println(fooOne.getName() + fooTwo.getName());
}
}
}
The above code doesn't work because there is no Foo bean named foo3. This is exactly what I expect. Now let's change Foo class definition to include a @Qualifier:
@Qualifier("foo3")
static class Foo {
// remaining code not changed..
With that change, the @Qualifier on Bar::setFoo method seems to be ignored and instances foo1 and foo2 are injected. No bean with id foo3 is present in the context. I have hard time understanding the mechanism applied here. Can anyone explain it?
By putting the qualifier on class Foo:
@Qualifier("foo3")
static class Foo {
...
}
Every bean instantiated as a class Foo will have the given qualifier. You are correct that are is not a single bean with id foo3, however there are two beans present with a qualifier foo3.
More information on the topic can be found here.
To inject both beans, remove the qualifier on the static class and add a qualifier on both parameters:
@Autowired
void setFoo(@Qualifier("foo1") Foo foo1, @Qualifier("foo2") Foo foo2) {
To play around with beans in an application context, I'd suggest injecting the application context and retrieving the beans to inspect them. For instance:
@SpringBootApplication
class DemoApplication {
@Autowired
ApplicationContext context
@PostConstruct
public void init() {
// Beans by qualifier
BeanFactoryAnnotationUtils.qualifiedBeanOfType(context.getBeanFactory(), YourClass.class, "foo3");
}
...
}