Автоматичне підключення списку за допомогою схеми утиліти дає NoSuchBeanDefinitionException


90

У мене є компонент, який я хочу ввести з іменованим списком, використовуючи простір імен <util:list id="myList">Spring, але Spring замість цього шукає колекцію бобів типу 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 :

Також можна надати всі компоненти певного типу ApplicationContext, додавши анотацію до поля або методу, який очікує масив цього типу ...

Те саме стосується типізованих колекцій ...

Іншими словами, кажучи @Autowired @Qualifier("myList") List<String>, ви насправді просите "надати мені список усіх бобів типу, java.lang.Stringякі мають кваліфікатор" myList ".

Рішення згадується в 3.11.3. Точне налаштування автопроводу на основі анотацій за допомогою кваліфікаторів :

Якщо ви маєте намір виразити ін'єкцію, керовану анотацією, не використовуйте, в першу чергу, @Autowiredнавіть якщо технічно здатна посилатися на ім'я компонента через @Qualifier значення. Натомість віддайте перевагу @Resource анотації JSR-250, яка семантично визначена для ідентифікації конкретного цільового компонента за його унікальним іменем, при цьому оголошений тип не має значення для процесу зіставлення.

Як конкретний наслідок цієї семантичної різниці, зерна, які самі визначаються як колекція або тип карти, не можна вводити, @Autowiredоскільки відповідність типів не застосовується належним чином до них. Використовуйте @Resourceдля таких бобів, посилаючись на конкретну колекцію / боб на карті за унікальною назвою.

Тож використовуйте це у своєму тесті, і воно працює нормально:

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

9
Ви безпечніше життя, як і stackoverflow.com! :)
Ріхардс

5
Я б дав ці десять голосів, якби міг. Ти Da Man, скафмане.
duffymo

3
Той факт, що багатьох це заплутало, означає, що семантика справді бентежить. Цікаво, що було причиною такого заплутаного дизайну.
supertonsky

3
Ого, я вважаю Весну одним із своїх найсильніших напрямків у програмуванні, і я ніколи не стикався з цією проблемою до сьогодні, і Ви заощадили мені купу часу. Дякую!
Аві

2
Будь-яка пропозиція щодо того, як змусити це працювати з введенням конструктора / методу, кажучи як @Resource не підтримує параметр?
cjbooms

0

Інша річ, яка може відбуватися, полягає в тому, що ви автоматично підключаєте властивість квасолі. У такому випадку вам не потрібно його автоматично підключати, а просто створіть метод встановлення та використовуйте тег властивостей у прикладі визначення компонента (при використанні 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анотації на уроці.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.