使用 util 模式自动连接 List 会产生 NoSuchBeanDefinition itionException

我有一个 bean,我想用 Spring util 命名空间 <util:list id="myList">注入一个命名列表,但是 Spring 正在寻找类型为 String 的 bean 的集合。我的失败测试是:

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


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


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

我的背景是:

<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>

但我明白

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)

这让我很困惑,因为我认为这将是预期的工作方式。

38533 次浏览

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;

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.