Joseph Mae Joseph Mae - 3 months ago 12
Java Question

How do you inject a list of beans with the same @Qualifier?

Here's my code:

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.util.List;

@Configuration
public class ResourceTest {

@Bean
TestBean testBean () {
return new TestBean();
}

@Bean
TargetBean targetBean () {
return new TargetBean("bean1");
}

@Bean(name = "myBean")
TargetBean targetBean2 () {
return new TargetBean("bean2");
}

@Bean(name = "myBean")
TargetBean targetBean3 () {
return new TargetBean("bean3");
}

public static void main (String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
ResourceTest.class);
TestBean bean = context.getBean(TestBean.class);
System.out.println(bean.getTargetBeanList());

}

public static class TestBean {
private List<TargetBean> targetBeanList;

@Resource(name = "myBean")
public void setXList (List<TargetBean> targetBeanList) {
this.targetBeanList = targetBeanList;
}

public List<TargetBean> getTargetBeanList () {
return targetBeanList;
}
}

public static class TargetBean {
private final String str;

public TargetBean (String str) {
this.str = str;
}

public String getStr () {
return str;
}

@Override
public String toString () {
return "TargetBean{ str='" + str + '\'' +
'}';
}
}
}


Output:
[TargetBean{ str='bean2'}]

Expected:
All beans qualifying "myBean" should be in the list i.e. bean2 and bean3.

According to spring ref docs:


Qualifiers also apply to typed collections, as discussed above, for example, to Set. In this case, all matching beans according to the declared qualifiers are injected as a collection. This implies that qualifiers do not have to be unique; they rather simply constitute filtering criteria. For example, you can define multiple MovieCatalog beans with the same qualifier value "action", all of which would be injected into a Set annotated with @Qualifier("action").


I'm using Spring 4.3.2.RELEASE. Please help fixing this. Thanks in advance.

Answer

The name in a @Bean annotated element represents the corresponding bean definition's identifier. If you have two bean definitions with the same id, the one registered later overwrites the previous one. That's what happens with your beans

@Bean(name = "myBean")
TargetBean targetBean2 () {
    return new TargetBean("bean2");
}

@Bean(name = "myBean")
TargetBean targetBean3 () {
    return new TargetBean("bean3");
}

You've linked that part of the doc but you're not doing any of the things it says.

For example, you can define multiple MovieCatalog beans with the same qualifier value "action"

Let's adapt that to your beans

@Bean(name = "myBean2")
@Qualifier("myBean")
TargetBean targetBean2() {
    return new TargetBean("bean2");
}

@Bean(name = "myBean3")
@Qualifier("myBean")
TargetBean targetBean3() {
    return new TargetBean("bean3");
}

Notice they have different ids (you can omit the @Bean annotation and Spring will use the method name) but are using the same @Qualifier value.

Then

all of which would be injected into a Set annotated with @Qualifier("action")

At your injection point, use that

@Autowired
@Qualifier("myBean")
public void setXList(List<TargetBean> targetBeanList) {
    this.targetBeanList = targetBeanList;
}

targetBeanList will now contain the two TargetBean beans that were qualified with myBean.


Spring resolves @Resource by using the name provided to lookup a bean by name. It has no knowledge of @Qualifier values. That's why we use @Autowired with @Qualifier above.

Comments