Автоматическое подключение списка с использованием схемы util дает исключение NoSuchBeanDefinitionException

90

У меня есть bean-компонент, который я хочу внедрить с именованным списком с использованием пространства имен Spring util, <util:list id="myList">но вместо этого Spring ищет коллекцию beans типа String. Мой сломанный тест:

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

Что меня скорее озадачивает, поскольку я полагал, что это будет именно так, как ожидалось.

Пол Маккензи
источник

Ответы:

171

Это связано с довольно непонятной частью поведения @ Autowired, указанной в 3.11.2. @Autowired :

Также возможно предоставить все bean-компоненты определенного типа из ApplicationContext, добавив аннотацию к полю или методу, который ожидает массив этого типа ...

То же самое и с типизированными коллекциями ...

Другими словами, говоря @Autowired @Qualifier("myList") List<String>, вы фактически просите «дать мне список всех bean-компонентов типа, java.lang.Stringкоторые имеют квалификатор myList».

Решение упоминается в 3.11.3. Тонкая настройка автоматического связывания на основе аннотаций с квалификаторами :

Если вы намереваетесь выразить внедрение, управляемое аннотациями, по имени, не используйте его в первую очередь @Autowired- даже если технически он может ссылаться на имя bean-компонента через @Qualifier значения. Вместо этого предпочтите @Resource аннотацию JSR-250, которая семантически определена для идентификации конкретного целевого компонента по его уникальному имени, при этом объявленный тип не имеет отношения к процессу сопоставления.

Как конкретное следствие этого семантического различия, bean-компоненты, которые сами определены как тип коллекции или карты, не могут быть введены через, @Autowiredпоскольку сопоставление типов к ним не применимо. Используйте @Resourceдля таких bean-компонентов, ссылаясь на конкретный bean-компонент коллекции / карты по уникальному имени.

Так что используйте это в своем тесте, и он отлично работает:

@Resource(name="myList") private List<String> stringList;
Скаффман
источник
9
Ваша жизнь безопаснее, и stackoverflow.com тоже! :)
Rihards
5
Я бы отдал за это десять голосов, если бы мог. Ты да Ман, скаффман.
duffymo
3
Тот факт, что многие были сбиты с толку этим, означает, что семантика действительно сбивает с толку. Интересно, в чем причина такого запутанного дизайна.
supertonsky
3
Вау, я считаю Spring одной из самых сильных сторон в программировании, и я никогда не сталкивался с этой проблемой до сегодняшнего дня, и вы сэкономили мне массу времени. Благодарность!
Avi
2
Любые предложения о том, как заставить это работать с внедрением конструктора / метода, говоря, что @Resource не поддерживает параметр?
cjbooms 08
0

Другая вещь, которая может произойти, - это то, что вы автоматически подключаете свойство bean-компонента. В таком случае вам не нужно его автоматически подключать, просто создайте метод установки и используйте тег свойства в примере определения bean (при использовании xml):

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

И класс:

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

Видишь, @Autowiredв классе нет аннотации.

Юлиангонсалес
источник