Il cablaggio automatico di un elenco utilizzando lo schema util fornisce NoSuchBeanDefinitionException


90

Ho un bean che voglio iniettare con un elenco denominato utilizzando lo spazio dei nomi Spring util <util:list id="myList">ma Spring sta cercando invece una raccolta di bean di tipo String. Il mio test non funzionante è:

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

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

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

Il mio contesto è:

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

Ma capisco

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)

Il che mi lascia piuttosto perplesso perché immaginavo che questo sarebbe stato il modo in cui avrebbe dovuto funzionare.

Risposte:


171

Ciò è dovuto a una parte piuttosto oscura del comportamento di @ Autowired, specificato in 3.11.2. @Autowired :

È anche possibile fornire tutti i bean di un tipo particolare ApplicationContextda aggiungendo l'annotazione a un campo o metodo che prevede un array di quel tipo ...

Lo stesso vale per le raccolte dattiloscritte ...

In altre parole, dicendo @Autowired @Qualifier("myList") List<String>, in realtà stai chiedendo "dammi l'elenco di tutti i bean di tipo java.lang.Stringche hanno il qualificatore" myList ".

La soluzione è menzionata in 3.11.3. Ottimizzazione del cablaggio automatico basato sull'annotazione con qualificatori :

Se si intende esprimere l'iniezione guidata dall'annotazione per nome, non utilizzare principalmente @Autowired, anche se è tecnicamente in grado di fare riferimento a un nome di bean tramite @Qualifier valori. Preferire invece l' @Resource annotazione JSR-250 che è definita semanticamente per identificare un componente di destinazione specifico con il suo nome univoco, con il tipo dichiarato irrilevante per il processo di corrispondenza.

Come conseguenza specifica di questa differenza semantica, i bean che sono definiti come una raccolta o un tipo di mappa non possono essere iniettati tramite @Autowiredpoiché la corrispondenza del tipo non è propriamente applicabile a loro. Utilizzare @Resourceper tali bean, facendo riferimento al bean di raccolta / mappa specifico con un nome univoco.

Quindi usa questo nel tuo test e funziona bene:

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

9
Sei una vita più sicura e lo è anche stackoverflow.com! :)
Rihards

5
Darei dieci voti se potessi. Sei Da Man, skaffman.
duffymo

3
Il fatto che molti siano stati confusi con questo significa che la semantica è davvero confusa. Mi chiedo quale fosse la ragione dietro un design così confuso.
supertonsky

3
Wow, considero la primavera una delle mie aree più forti nella programmazione e non ho mai riscontrato questo problema fino ad oggi e mi hai risparmiato un sacco di tempo. Grazie!
Avi

2
Qualche suggerimento su come farlo funzionare con l'iniezione di costruttore / metodo, dicendo che @Resource non supporta il parametro?
cjbooms

0

Un'altra cosa che potrebbe accadere è che stai autowiring della proprietà di un fagiolo. In tal caso non è necessario autowire, ma è sufficiente creare il metodo setter e utilizzare il tag della proprietà nell'esempio di definizione del bean (quando si utilizza 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>

E la classe:

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

Vedi, nessuna @Autowiredannotazione in classe.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.