Automatyczne łączenie listy przy użyciu schematu util daje wyjątek NoSuchBeanDefinitionException

90

Mam fasolę, którą chcę wstrzyknąć z nazwaną listą przy użyciu przestrzeni nazw użytkowych <util:list id="myList">Spring, ale zamiast tego Spring szuka kolekcji fasoli typu String. Mój uszkodzony test to:

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

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

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

Mój kontekst to:

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

Ale rozumiem

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)

Co mnie bardziej zastanawia, ponieważ pomyślałem, że tak powinno działać.

Paul McKenzie
źródło

Odpowiedzi:

171

Wynika to z raczej niejasnej części zachowania @ Autowired, opisanej w 3.11.2. @Autowired :

Możliwe jest również dostarczenie wszystkich ziaren określonego typu z elementu ApplicationContextprzez dodanie adnotacji do pola lub metody, która oczekuje tablicy tego typu ...

To samo dotyczy kolekcji wpisanych na maszynie ...

Innymi słowy, mówiąc @Autowired @Qualifier("myList") List<String>, w rzeczywistości prosisz o „podaj mi listę wszystkich ziaren typu, java.lang.Stringktóre mają kwalifikator„ myList ”.

Rozwiązanie jest wymienione w 3.11.3. Dostrajanie automatycznego okablowania opartego na adnotacjach z kwalifikatorami :

Jeśli zamierzasz wyrazić iniekcję opartą na adnotacjach według nazwy, nie używaj jej głównie @Autowired- nawet jeśli jest technicznie zdolny do odniesienia się do nazwy ziarna za pomocą @Qualifier wartości. Zamiast tego preferuj @Resource adnotację JSR-250, która jest zdefiniowana semantycznie, aby identyfikować określony komponent docelowy za pomocą jego unikalnej nazwy, przy czym zadeklarowany typ nie ma znaczenia dla procesu dopasowywania.

Specyficzną konsekwencją tej różnicy semantycznej jest to, że ziarna, które same są zdefiniowane jako kolekcja lub typ mapy, nie mogą być wstrzykiwane za pośrednictwem, @Autowiredponieważ dopasowywanie typów nie jest do nich odpowiednio stosowane. Użyj @Resourcedla takich fasoli, odnosząc się do określonej kolekcji / mapy fasoli za pomocą unikalnej nazwy.

Więc użyj tego w swoim teście i działa dobrze:

@Resource(name="myList") private List<String> stringList;
skaffman
źródło
9
Jesteś bezpieczniejszy, podobnie jak stackoverflow.com! :)
Rihards
5
Gdybym mógł, oddałbym te dziesięć głosów. Jesteś Da Man, skaffman.
duffymo
3
Fakt, że wielu zostało przez to zdezorientowanych, oznacza, że ​​semantyka jest naprawdę zagmatwana. Zastanawiam się, co było przyczyną takiego zagmatwanego projektu.
supertonsky
3
Wow, uważam Spring za jeden z moich najsilniejszych obszarów programowania i do dzisiaj nie spotkałem się z tym problemem, a Ty zaoszczędziłeś mi mnóstwo czasu. Dzięki!
Avi
2
Jakieś sugestie, jak sprawić, aby to działało z iniekcją konstruktora / metody, mówiąc, że @Resource nie obsługuje parametru?
cjbooms
0

Inną rzeczą, która może się zdarzyć, jest to, że automatycznie podłączasz właściwość fasoli. W takim przypadku nie musisz go autowire'ować, ale po prostu utwórz metodę ustawiającą i użyj tagu property w definicji beana (w przypadku korzystania z xml) przykład:

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

I klasa:

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

Widzisz, @Autowiredna zajęciach nie ma adnotacji.

juliangonzalez
źródło