Cos'è esattamente l'iniezione di campo e come evitarla?


130

Ho letto in alcuni post su Spring MVC e Portlet che l' iniezione di campo non è consigliata. A quanto ho capito, l' iniezione sul campo è quando inietti un fagiolo con @Autowiredquesto:

@Component
public class MyComponent {
    @Autowired
    private Cart cart;
}

Durante la mia ricerca ho letto anche sull'iniezione del costruttore :

@Component
public class MyComponent {
    private final Cart cart;

    @Autowired
    public MyComponent(Cart cart){
       this.cart = cart;
    }
}

Quali sono i vantaggi e gli svantaggi di entrambi questi tipi di iniezioni?


EDIT 1: Poiché questa domanda è contrassegnata come duplicata di questa domanda, l' ho controllata. Perché non ci sono esempi di codice né nella domanda né nelle risposte, non è chiaro per me se ho ragione con la mia ipotesi su quale tipo di iniezione sto usando.


3
Se l'iniezione sul campo è così negativa come descrivi, perché la primavera lo consente? Field injection ha i suoi vantaggi, nel rendere il codice più leggibile e meno dettagliato. Se sei abbastanza disciplinato nella tua codifica, puoi essere certo che le cose non si rompono anche se usi l'iniezione sul campo.
ceneri

@ ciglia Perché all'epoca era una caratteristica interessante e le implicazioni non erano state completamente pensate. Lo stesso motivo che Date(int,int,int)esiste.
chrylis -cautiouslyoptimistic-

Risposte:


225

Tipi di iniezione

Ci sono tre opzioni su come le dipendenze possono essere iniettate in un bean:

  1. Tramite un costruttore
  2. Tramite setter o altri metodi
  3. Attraverso la riflessione, direttamente nei campi

Stai usando l'opzione 3. Questo è ciò che accade quando usi @Autowireddirettamente sul tuo campo.


Linee guida per l'iniezione

Una linea guida generale, raccomandata da Spring (vedere le sezioni su DI basata su costruttore o DI basata su Setter ) è la seguente:

  • Per le dipendenze obbligatorie o quando si punta all'immutabilità, utilizzare l'iniezione del costruttore
  • Per dipendenze facoltative o modificabili, utilizzare l'iniezione di setter
  • Evita l'iniezione sul campo nella maggior parte dei casi

Inconvenienti dell'iniezione di campo

I motivi per cui l'iniezione sul campo è disapprovata sono i seguenti:

  • Non puoi creare oggetti immutabili, come puoi fare con l'iniezione nel costruttore
  • Le tue classi hanno un accoppiamento stretto con il tuo contenitore DI e non possono essere utilizzate al di fuori di esso
  • Le tue classi non possono essere istanziate (ad esempio negli unit test) senza riflessione. Hai bisogno del contenitore DI per istanziarli, il che rende i tuoi test più simili ai test di integrazione
  • Le tue reali dipendenze sono nascoste dall'esterno e non si riflettono nella tua interfaccia (costruttori o metodi)
  • È davvero facile avere dieci dipendenze. Se stavi usando l'iniezione del costruttore, avresti un costruttore con dieci argomenti, che segnalerebbero che qualcosa è sospetto. Ma puoi aggiungere campi iniettati usando l'inserimento di campi a tempo indeterminato. Avere troppe dipendenze è una bandiera rossa che la classe di solito fa più di una cosa e che potrebbe violare il Principio di responsabilità unica.

Conclusione

A seconda delle tue esigenze, dovresti usare principalmente l'iniezione del costruttore o una combinazione di iniezione del costruttore e setter. L'iniezione sul campo presenta molti inconvenienti e dovrebbe essere evitata. L'unico vantaggio dell'iniezione sul campo è che è più conveniente scrivere, il che non supera tutti i contro.


Ulteriore lettura

Ho scritto un articolo sul blog sul motivo per cui l'iniezione di campo di solito non è consigliata: Iniezione di dipendenza dal campo considerata dannosa .


12
In generale non è una buona idea e non è carino dire al mondo che "l'iniezione sul campo dovrebbe essere evitata". Mostra pro e contro e lascia che gli altri decidano da soli;) Molte persone hanno altre esperienze e il proprio modo di vedere le cose.
Dieter

6
Potrebbe essere il caso qui, ma ci sono altri casi in cui la comunità è arrivata a un consenso generale per scoraggiare qualcosa. Prendi, ad esempio, la notazione ungherese.
Jannik

Dai alcuni buoni punti come testabilità e visibilità delle dipendenze, ma non sono d'accordo con tutti. L'iniezione nel costruttore non ha svantaggi? Avere 5 o 6 campi da iniettare in classe che esegue vere composizioni di chiamata può essere desiderabile. Non sono d'accordo anche con te sull'immutabilità. Avere campi finali non è obbligatorio per avere una classe immutabile. È preferibile. Che è molto diverso.
davidxxx

Penso che intendessi "Per dipendenze obbligatorie o quando si punta all'immutabilità "
Alex Terreaux

1
Mi riferivo al collegamento all'inizio della risposta che collega ai documenti di primavera
Vojtech Ruzicka

47

Questa è una delle discussioni senza fine nello sviluppo del software, ma i principali influencer del settore stanno diventando più supponenti sull'argomento e hanno iniziato a suggerire l'iniezione del costruttore come l'opzione migliore.

Iniezione nel costruttore

Professionisti:

  • Migliore testabilità . Non è necessaria alcuna libreria beffarda o un contesto Spring nei test unitari. È possibile creare un oggetto che si desidera testare con la nuova parola chiave. Tali test sono sempre più veloci perché non si basano sul meccanismo di riflessione. ( Questa domanda è stata posta 30 minuti dopo. Se l'autore avesse usato l'iniezione del costruttore non sarebbe apparsa).
  • Immutabilità . Una volta impostate le dipendenze, non è possibile modificarle.
  • Codice più sicuro . Dopo l'esecuzione di un costruttore il tuo oggetto è pronto per l'uso poiché puoi convalidare tutto ciò che è stato passato come parametro. L'oggetto può essere pronto o meno, non esiste uno stato intermedio. Con l'iniezione di campo si introduce una fase intermedia quando l'oggetto è fragile.
  • Espressione più pulita delle dipendenze obbligatorie . L'iniezione sul campo è ambigua in questa materia.
  • Fa riflettere gli sviluppatori sul design . dit ha scritto di un costruttore con 8 parametri, che in realtà è il segno di un cattivo design e dell'anti-pattern dell'oggetto Dio . Non importa se una classe ha 8 dipendenze nel suo costruttore o nei campi, è sempre sbagliato. Le persone sono più riluttanti ad aggiungere più dipendenze a un costruttore rispetto ai campi. Funziona come un segnale per il tuo cervello che dovresti fermarti per un po 'e pensare alla struttura del tuo codice.

Contro:

  • Più codice (ma gli IDE moderni alleviano il dolore).

Fondamentalmente, l'iniezione di campo è l'opposto.


1
testabilità, sì, è stato un incubo per me prendere in giro i fagioli iniettati nel campo. Una volta, ho usato l'iniezione di contsructor, non ho bisogno di fare inutili derisioni
kenobiwan

25

Questione di gusti. È una tua decisione.

Ma posso spiegare perché non uso mai l' iniezione del costruttore .

  1. Non voglio implementare un costruttore per tutti i miei @Service, @Repositorye @Controllerfagioli. Voglio dire, ci sono circa 40-50 fagioli o più. Ogni volta che aggiungo un nuovo campo dovrei estendere il costruttore. No. Non lo voglio e non devo.

  2. Cosa succede se il tuo Bean (servizio o controller) richiede l'iniezione di molti altri fagioli? Un costruttore con 4+ parametri è molto brutto.

  3. Se sto usando CDI, il costruttore non mi riguarda.


EDIT # 1 : Vojtech Ruzicka ha detto:

class ha troppe dipendenze e probabilmente viola il principio di responsabilità unica e dovrebbe essere riformattato

Sì. Teoria e realtà. Ecco un esempio: DashboardControllermappato a percorso singolo *:8080/dashboard.

My DashboardControllerraccoglie molte informazioni da altri servizi per visualizzarle in una dashboard / pagina di panoramica del sistema. Ho bisogno di questo controller singolo. Quindi devo proteggere solo questo percorso (autenticazione di base o filtro del ruolo utente).

EDIT # 2 : poiché tutti sono concentrati sugli 8 parametri nel costruttore ... Questo era un esempio del mondo reale: un codice legacy dei clienti. L'ho cambiato. La stessa argomentazione si applica a me per 4+ parametri.

Si tratta di iniezione di codice, non di costruzione di istanze.


34
Un brutto costruttore con 8 dipendenze è davvero fantastico in quanto è una bandiera rossa che qualcosa non va, la classe ha troppe dipendenze e probabilmente sta violando il principio di responsabilità singola e dovrebbe essere refactored. In realtà è una buona cosa.
Vojtech Ruzicka

6
@VojtechRuzicka non è certo carino ma a volte non puoi evitarlo.
dieta

4
Direi che una regola pratica di 3, figuriamoci 40-50, le dipendenze per qualsiasi classe dovrebbero essere un segno che è necessario eseguire il refactoring. Non è possibile che una classe con 40 dipendenze si attenga all'entità di responsabilità singola o all'entità di apertura / chiusura.
Amin J

4
@AminJ La regola è fantastica, ma la realtà è diversa. L'azienda in cui lavoro è da oltre 20 anni e abbiamo molto codice legacy. Il refactoring è una buona idea, ma costa denaro. Inoltre non so perché lo dico ma non intendevo 40-50 dipendenze, intendo 40-50 bean, componenti, moduli ...
dieter

7
@dit, la tua situazione è chiaramente quella in cui il debito tecnico ti sta portando a fare scelte non ottimali. Con le tue stesse parole, ti trovi in ​​una situazione in cui il tuo processo decisionale è notevolmente influenzato dal codice legacy di oltre 20 anni. All'inizio di un nuovo progetto, consiglieresti comunque l'iniezione sul campo anziché l'iniezione del costruttore? Forse dovresti mettere un avvertimento nella tua risposta per indicare in quali casi sceglieresti l'iniezione sul campo.
Umar Farooq Khawaja

0

Un altro commento: Vojtech Ruzicka ha affermato che Spring inietta i fagioli in questi tre modi (la risposta con il maggior numero di punti):

  1. Tramite un costruttore
  2. Tramite setter o altri metodi
  3. Attraverso la riflessione, direttamente nei campi

Questa risposta è SBAGLIATA, perché PER OGNI TIPO DI INIEZIONE LA MOLLA UTILIZZA LA RIFLESSIONE! Usa IDE, imposta il punto di interruzione sul setter / costruttore e controlla.

Questa può essere una questione di gusti ma può anche essere una questione di CASO. @dieter ha fornito un ottimo caso in cui l'iniezione sul campo è migliore. Se stai utilizzando l'iniezione sul campo nei test di integrazione che stanno configurando il contesto Spring, anche l'argomento con testabilità della classe non è valido, a meno che tu non voglia scrivere successivamente sui test nei tuoi test di integrazione;)

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.