I have a configuration class as below:
@Configuration
public class ListConfiguration {
@Bean
public List<Integer> list() {
List<Integer> ints = new ArrayList<>();
ints.add(1);
ints.add(2);
ints.add(3);
return ints;
}
@Bean
public int number() {
return 4;
}
}
I also have a test class as below
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = ListConfiguration.class)
public class ListTest {
@Autowired
List<Integer> ints;
@Test
public void print() {
System.out.println(ints.size());
System.out.println(ints);
}
}
But the output of the print
method is 1
and [4]
, why not 3
and [1,2,3]
? Thank you very much for any help!
1. Enable configuration to use @Autowired By declaring all the beans in Spring Configuration file, Spring container can autowire relationships between collaborating beans. Following are the various configuration styles can be used to use @Autowired in Spring applications and you can use mixed configurations within same application.
Autowiring ‘byType’: This option enables the autowire based on bean type. It searches property’s class type in configuration file. It injects the property, if such bean is found, otherwise an error is raised. . 4. Autowiring ‘constructor’ but it applies to constructor arguments.
The Spring framework enables automatic dependency injection. In other words, by declaring all the bean dependencies in a Spring configuration file, Spring container can autowire relationships between collaborating beans. This is called Spring bean autowiring.
The XML configuration based autowiring functionality has five modes – no no, byName byName, byType byType, constructor constructor, and autodetect autodetect. The default mode is no no.
You've got a bean of type Integer
and a bean of type List<Integer>
in your application context.
Now obviously the bean you want to autowire is of type List<Integer>
, which does qualify as a candidate for autowiring. To discover how Spring actually autowires fields I had to dive deep into the AutowiredAnnotationBeanPostProcessor
class.
TL;DR of my investigation is that Spring will prefer to autowire objects in the following order:
@Value
That means that if you're autowiring a List<Integer>
Spring will attempt to autowire multiple Integer
beans into the list before it will attempt to autowire a single List<Integer>
bean.
You can see this behaviour in the DefaultListableBeanFactory
class.
Relevant snippet below:
public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
Class<?> type = descriptor.getDependencyType();
//Searches for an @Value annotation and
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
//Handle finding, building and returning default value
}
/*
* Check for multiple beans of given type. Because a bean is returned here,
* Spring autowires the Integer bean instance.
*/
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
// Do more stuff here to try and narrow down to a single instance to autowire.
}
}
Hopefully this explains why you do need to use an @Qualifer
annotation when trying to autowire a list of a type when you've got individual beans of that type in your application context.
EDIT:
It's worth noting that this is not good practice. Creating a collection of primitives or primitive wrappers and registering it as a bean is going to cause issues. The best way to do this is with @Value
and define your list of primitives in a properties file, that Spring picks up.
Example:
application.properties file
list=1,2,3,4
In your config class declare the following bean:
@Bean
public ConversionService conversionService() {
return new DefaultConversionService();
}
The default conversion service is used to convert comma separated values declared in a properties file into a collection of objects with type safety.
Class to use it:
@Value("${list}")
private List<Integer> anotherList;
anotherList
will contain 1,2,3 & 4 as elements in the list.
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