Auto-wiring a List using util schema gives NoSuchBeanDefinitionException

JavaSpring

Java Problem Overview


I have a bean that i want to inject with a named list using Spring util namespace <util:list id="myList"> but Spring is looking for a collection of beans of type String instead. My broken test is:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class ListInjectionTest {

    @Autowired @Qualifier("myList") private List<String> stringList;

    @Test public void testNotNull() {
        TestCase.assertNotNull("stringList not null", stringList);
    }
}

My context is:

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:util="http://www.springframework.org/schema/util"
   xmlns="http://www.springframework.org/schema/beans"
   xmlns:context="http://www.springframework.org/schema/context"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
   http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

   <util:list id="myList">
       <value>foo</value>
       <value>bar</value>
   </util:list>

</beans>

But I get

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [java.lang.String] found for dependency [collection of java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=myList)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:726)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:571)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:412)

Which puzzles me rather as I figured this would be the way it was expected to work.

Java Solutions


Solution 1 - Java

This is due to a rather obscure part of @Autowired's behaviour, specified in 3.11.2. @Autowired:

> It is also possible to provide all > beans of a particular type from the > ApplicationContext by adding the > annotation to a field or method that > expects an array of that type...

> The same applies for typed collections...

In other words, by saying @Autowired @Qualifier("myList") List<String>, you're actually asking for "give me the list of all beans of type java.lang.String that have the qualifier "myList".

The solution is mentioned in 3.11.3. Fine-tuning annotation-based autowiring with qualifiers:

> If you intend to express > annotation-driven injection by name, > do not primarily use @Autowired - even > if is technically capable of referring > to a bean name through @Qualifier > values. Instead, prefer the JSR-250 > @Resource annotation which is > semantically defined to identify a > specific target component by its > unique name, with the declared type > being irrelevant for the matching > process. > > As a specific consequence of this > semantic difference, beans which are > themselves defined as a collection or > map type cannot be injected via > @Autowired since type matching is not > properly applicable to them. Use > @Resource for such beans, referring to > the specific collection/map bean by > unique name.

So use this in your test, and it works fine:

@Resource(name="myList") private List<String> stringList;

Solution 2 - Java

Another thing that could be happening is that you are autowiring a property of a bean. In such case you dont need to autowire it, but just create the setter method and use the property tag in the bean definition (when using xml) example:

<bean id="cleaningUpOldFilesTasklet" class="com.example.mypackage.batch.tasklets.CleanUpOldFilesTasklet">
    <property name="directoriesToClean">
        <list>
            <value>asfs</value>
            <value>fvdvd</value>
            <value>sdfsfcc</value>
            <value>eeerer</value>
            <value>rerrer</value>
        </list>
    </property>
</bean>

And the class:

public class CleanUpOldFilesTasklet extends TransferingFilesTasklet implements Tasklet{

private long pastMillisForExpiration;
private final String dateFormat = "MM.dd";
Date currentDate = null;

List<String> directoriesToClean;

public void setDirectoriesToClean(List<String> directories){
    List<String> dirs = new ArrayList<>();
    for(String directory : directories){
        dirs.add(getSanitizedDir(directory));
    }
    this.directoriesToClean = dirs;
}

See, no @Autowired annotation in class.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPaul McKenzieView Question on Stackoverflow
Solution 1 - JavaskaffmanView Answer on Stackoverflow
Solution 2 - JavajuliangonzalezView Answer on Stackoverflow