Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to @Autowired a List<Integer> in spring framework

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!

like image 713
walsh Avatar asked Sep 23 '16 05:09

walsh


People also ask

How do I use @AutoWired in spring?

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.

What is autowire by type in Java?

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.

What is spring bean autowiring?

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.

What are the different modes of autowiring in XML?

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.


1 Answers

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:

  1. Default Value using @Value
  2. Multiple beans using a type parameter.
  3. Individual beans that match the field type.

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.

like image 137
JamesENL Avatar answered Sep 27 '22 17:09

JamesENL