Auto-wiring a List using util schema gives NoSuchBeanDefinitionException
JavaSpringJava 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.