Die automatische Verkabelung einer Liste mithilfe des util-Schemas führt zu NoSuchBeanDefinitionException

90

Ich habe eine Bean, die ich mit einer benannten Liste unter Verwendung des Spring-Util-Namespace injizieren möchte, <util:list id="myList">aber Spring sucht stattdessen nach einer Sammlung von Beans vom Typ String. Mein gebrochener Test ist:

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

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

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

Mein Kontext ist:

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

Aber ich verstehe

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)

Was mich eher verwirrt, als ich dachte, dass dies so funktionieren würde, wie es erwartet wurde.

Paul McKenzie
quelle

Antworten:

171

Dies ist auf einen eher undurchsichtigen Teil des Verhaltens von @ Autowired zurückzuführen, der in 3.11.2 angegeben ist. @Autowired :

Es ist auch möglich, alle Beans eines bestimmten Typs aus dem ApplicationContextFeld bereitzustellen, indem die Anmerkung zu einem Feld oder einer Methode hinzugefügt wird, die ein Array dieses Typs erwartet ...

Gleiches gilt für typisierte Sammlungen ...

Mit anderen Worten, wenn Sie sagen @Autowired @Qualifier("myList") List<String>, fragen Sie tatsächlich nach "Geben Sie mir die Liste aller Bohnen vom Typ java.lang.String, die das Qualifikationsmerkmal" myList "haben.

Die Lösung wird in 3.11.3 erwähnt. Feinabstimmung der annotationsbasierten automatischen Verdrahtung mit Qualifikationsmerkmalen :

Wenn Sie eine annotationsgesteuerte Injektion namentlich ausdrücken möchten, verwenden Sie sie nicht primär @Autowired- auch wenn Sie technisch in der Lage sind, durch @Qualifier Werte auf einen Bean-Namen zu verweisen . Bevorzugen Sie stattdessen die JSR-250- @Resource Annotation, die semantisch definiert ist, um eine bestimmte Zielkomponente anhand ihres eindeutigen Namens zu identifizieren, wobei der deklarierte Typ für den Abgleichsprozess irrelevant ist.

Als spezifische Konsequenz dieses semantischen Unterschieds können Beans, die selbst als Sammlung oder Kartentyp definiert sind, nicht über injiziert werden, @Autowiredda der Typabgleich für sie nicht richtig anwendbar ist. Verwendung @Resourcefür solche Beans unter Bezugnahme auf die spezifische Collection / Map-Bean mit eindeutigem Namen.

Verwenden Sie dies also in Ihrem Test, und es funktioniert gut:

@Resource(name="myList") private List<String> stringList;
Skaffman
quelle
9
Sie sind ein Leben sicherer und Stackoverflow.com auch! :)
Rihards
5
Ich würde diese zehn Stimmen geben, wenn ich könnte. Du bist Da Man, Skaffman.
Duffymo
3
Die Tatsache, dass viele dadurch verwirrt wurden, bedeutet, dass die Semantik wirklich verwirrend ist. Ich frage mich, was der Grund für dieses verwirrende Design war.
Supertonsky
3
Wow, ich betrachte Spring als einen meiner stärksten Bereiche in der Programmierung und ich bin bis heute noch nie auf dieses Problem gestoßen, und Sie haben mir jede Menge Zeit gespart. Vielen Dank!
Avi
2
Gibt es einen Vorschlag, wie dies mit der Konstruktor- / Methodeninjektion funktioniert, da @Resource keine Parameter unterstützt?
cjbooms
0

Eine andere Sache, die passieren könnte, ist, dass Sie eine Eigenschaft einer Bohne automatisch verdrahten. In diesem Fall müssen Sie es nicht automatisch verdrahten, sondern erstellen einfach die Setter-Methode und verwenden das Eigenschaftstag in der Bean-Definition (bei Verwendung von XML). Beispiel:

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

Und die Klasse:

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

Siehe, keine @AutowiredAnmerkung in der Klasse.

juliangonzalez
quelle