Comprensione di Spring @Autowired use


309

Sto leggendo la documentazione di riferimento di Spring 3.0.x per comprendere l'annotazione Spring autowired:

3.9.2 @Autowired e @Inject

Non sono in grado di comprendere gli esempi seguenti. Dobbiamo fare qualcosa nell'XML affinché funzioni?

ESEMPIO 1

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

ESEMPIO 2

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(MovieCatalog movieCatalog,
                    CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

Come possono essere autowired le due classi implementando la stessa interfaccia e usando la stessa classe?

Esempio:

class Red implements Color
class Blue implements Color

class myMainClass{
    @Autowired 
    private Color color;

    draw(){
        color.design(); 
    } 
}

Quale metodo di progettazione verrà chiamato? Come posso assicurarmi che venga chiamato il metodo di progettazione della classe Red e non Blue?

Risposte:


542

TL; DR

L'annotazione @Autowired ti risparmia la necessità di eseguire il cablaggio da solo nel file XML (o in qualsiasi altro modo) e trova semplicemente per te ciò che deve essere iniettato dove, e lo fa per te.

Spiegazione completa

L' @Autowiredannotazione ti consente di saltare le configurazioni altrove su cosa iniettare e lo fa solo per te. Supponendo che il tuo pacchetto sia che com.mycompany.moviesdevi inserire questo tag nel tuo XML (file di contesto dell'applicazione):

<context:component-scan base-package="com.mycompany.movies" />

Questo tag eseguirà una scansione automatica. Supponendo che ogni classe che deve diventare un bean sia annotata con un'annotazione corretta come @Component(per un bean semplice) o @Controller(per un controllo servlet) o @Repository(per le DAOclassi) e queste classi sono da qualche parte sotto il pacchetto com.mycompany.movies, Spring troverà tutte queste e creerà un fagiolo per ognuno. Questo viene fatto in 2 scansioni delle classi: la prima volta cerca solo le classi che devono diventare un bean e mappa le iniezioni che deve fare e alla seconda scansione inietta i bean. Ovviamente, puoi definire i tuoi bean nel file XML più tradizionale o con una classe @Configuration (o qualsiasi combinazione dei tre).

L' @Autowiredannotazione dice a Spring dove deve avvenire un'iniezione. Se lo metti su un metodo setMovieFinder, capisce (tramite il prefisso set+ l' @Autowiredannotazione) che un bean deve essere iniettato. Nella seconda scansione, Spring cerca un bean di tipo MovieFindere, se trova tale bean, lo inietta in questo metodo. Se trova due di questi fagioli otterrai un Exception. Per evitare il Exception, puoi usare l' @Qualifierannotazione e dirgli quale dei due bean iniettare nel modo seguente:

@Qualifier("redBean")
class Red implements Color {
   // Class code here
}

@Qualifier("blueBean")
class Blue implements Color {
   // Class code here
}

O se preferisci dichiarare i bean nel tuo XML, sarebbe simile a questo:

<bean id="redBean" class="com.mycompany.movies.Red"/>

<bean id="blueBean" class="com.mycompany.movies.Blue"/>

Nella @Autowireddichiarazione, è inoltre necessario aggiungere il @Qualifierper indicare quale dei due bean di colore iniettare:

@Autowired
@Qualifier("redBean")
public void setColor(Color color) {
  this.color = color;
}

Se non si desidera utilizzare due annotazioni (la @Autowirede @Qualifier), è possibile utilizzare @Resourceper combinare queste due:

@Resource(name="redBean")
public void setColor(Color color) {
  this.color = color;
}

Il @Resource(puoi leggere alcuni dati extra a riguardo nel primo commento su questa risposta) ti risparmia l'uso di due annotazioni e invece ne usi solo una.

Aggiungerò solo altri due commenti:

  1. La buona pratica sarebbe usare @Injectinvece che @Autowiredperché non è specifico per la primavera e fa parte dello JSR-330standard .
  2. Un'altra buona pratica sarebbe quella di mettere il @Inject/ @Autowiredsu un costruttore invece di un metodo. Se lo metti su un costruttore, puoi validare che i bean iniettati non sono null e falliscono velocemente quando provi ad avviare l'applicazione ed evita un NullPointerExceptionquando devi effettivamente usare il bean.

Aggiornamento : per completare l'immagine, ho creato una nuova domanda sulla @Configurationclasse.


6
Solo per completare la tua fantastica risposta: '@Resource' fa parte dello standard JSR-250 e ha una semantica aggiuntiva oltre alla semplice iniezione (Come hai detto '@Autowired' è di primavera; e '@Inject' fa parte del JSR-330) :)
Ignacio Rubio

Se MovieFinderè un'interfaccia e abbiamo un bean per MovieFinderImpl(bean id = movieFinder), Spring lo inietterà automaticamente per tipo o per nome?
Jaskey,

@jaskey - dipende dal tuo utilizzo @Qualifier. Se lo fai - per nome, in caso contrario - per tipo. Per tipo funzionerebbe solo se MovieFindernel tuo contesto fosse presente un solo bean di tipo . Più di 1 porterebbe a un'eccezione.
Avi

@Avi, risposta eccezionale. Ma non capisco come funziona l' @Autowiredannotazione sul preparemetodo nell'esempio 2 . Sta inizializzando MovieRecommenderma, tecnicamente, NON è un setter.
Karan Chadha,

@KaranChadha - @AutowiredFunziona anche per i costruttori. Trova le dipendenze richieste e le inietta al costruttore.
Avi,

21

Nulla nell'esempio dice che le "classi implementano la stessa interfaccia". MovieCatalogè un tipo ed CustomerPreferenceDaoè un altro tipo. La primavera può facilmente distinguerli.

Nella primavera 2.x, il cablaggio dei bean avveniva principalmente tramite ID o nomi dei bean. Questo è ancora supportato da Spring 3.x ma spesso avrai un'istanza di un bean con un certo tipo - la maggior parte dei servizi sono singoli. Creare nomi per quelli è noioso. Quindi Spring ha iniziato a supportare "autowire per tipo".

Ciò che gli esempi mostrano sono vari modi che è possibile utilizzare per iniettare bean in campi, metodi e costruttori.

L'XML contiene già tutte le informazioni di cui Spring ha bisogno in quanto è necessario specificare il nome completo della classe in ciascun bean. Devi stare un po 'attento con le interfacce, però:

Questo autowiring fallirà:

 @Autowired
 public void prepare( Interface1 bean1, Interface1 bean2 ) { ... }

Poiché Java non mantiene i nomi dei parametri nel codice byte, Spring non può più distinguere tra i due bean. La correzione è di usare @Qualifier:

 @Autowired
 public void prepare( @Qualifier("bean1") Interface1 bean1,
     @Qualifier("bean2")  Interface1 bean2 ) { ... }

@AaronDigulla È stato bello. Tuttavia, voglio sapere come si chiama la funzione prepare, quali parametri verranno utilizzati per chiamare questa funzione?
Nguyen Quang Anh,

@NguyenQuangAnh Non sto chiamando il metodo, Spring lo farà quando viene creato il bean. Questo accade esattamente quando @Autowiredvengono iniettati i campi. Spring vedrà quindi che i parametri sono necessari e utilizzerà le stesse regole utilizzate per l'iniezione sul campo per trovare i parametri.
Aaron Digulla,

5

Sì, è possibile configurare il file xml del contesto servlet Spring per definire i bean (ovvero le classi), in modo che possa eseguire l'iniezione automatica per l'utente. Tuttavia, tieni presente che devi avere altre configurazioni per avere Spring up e funzionanti e il modo migliore per farlo è seguire un tutorial da zero.

Dopo aver configurato Spring probabilmente, è possibile eseguire le seguenti operazioni nel file XML del contesto servlet Spring per l'esempio 1 sopra riportato ( sostituire il nome del pacchetto di com.movies con il nome del pacchetto vero e se si tratta di una terza parte classe, quindi assicurarsi che il file jar appropriato sia sul percorso di classe):

<beans:bean id="movieFinder" class="com.movies.MovieFinder" />

o se la classe MovieFinder ha un costruttore con un valore primitivo, allora potresti fare qualcosa del genere,

<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
    <beans:constructor-arg value="100" />
</beans:bean>

o se la classe MovieFinder ha un costruttore in attesa di un'altra classe, allora potresti fare qualcosa del genere,

<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
    <beans:constructor-arg ref="otherBeanRef" />
</beans:bean>

... dove " otherBeanRef " è un altro bean che ha un riferimento alla classe prevista.


4
La definizione di tutti i cablaggi nell'XML manca solo dell'idea di@Autowired
Avi,
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.