Was genau ist Feldinjektion und wie kann man sie vermeiden?

130

Ich las in einigen Beiträgen über Spring MVC und Portlets , dass Feldeinkopplung nicht empfohlen. So wie ich es verstehe, ist Feldinjektion, wenn Sie einer Bohne Folgendes injizieren @Autowired:

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

Während meiner Recherche las ich auch über Konstruktorinjektion :

@Component
public class MyComponent {
    private final Cart cart;

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

Was sind die Vor- und Nachteile dieser beiden Arten von Injektionen?


EDIT 1: Da diese Frage als Duplikat dieser Frage markiert ist , habe ich sie überprüft. Da es weder in der Frage noch in den Antworten Codebeispiele gibt, ist mir nicht klar, ob ich mit meiner Vermutung, welchen Injektionstyp ich verwende, richtig bin.

T. Jung
quelle
3
Wenn die Feldinjektion so schlecht ist, wie Sie es beschreiben, warum erlaubt Spring es? Die Feldinjektion hat ihre eigenen Vorteile, da sie den Code lesbarer und weniger ausführlich macht. Wenn Sie in Ihrer Codierung diszipliniert genug sind, können Sie sicher sein, dass die Dinge auch bei Verwendung der Feldinjektion nicht kaputt gehen.
Asche
@ashes Weil es zu dieser Zeit eine nette Funktion war und die Auswirkungen nicht vollständig durchdacht waren. Der gleiche Grund, der Date(int,int,int)existiert.
Chrylis

Antworten:

224

Einspritzarten

Es gibt drei Möglichkeiten, wie Abhängigkeiten in eine Bean eingefügt werden können:

  1. Durch einen Konstruktor
  2. Durch Setter oder andere Methoden
  3. Durch Reflexion direkt in Felder

Sie verwenden Option 3. Dies geschieht, wenn Sie @Autowireddirekt auf Ihrem Feld verwenden.


Injektionsrichtlinien

Eine allgemeine Richtlinie, die von Spring empfohlen wird (siehe Abschnitte zu konstruktorbasiertem DI oder Setter-basiertem DI ), lautet wie folgt:

  • Verwenden Sie für obligatorische Abhängigkeiten oder wenn Sie Unveränderlichkeit anstreben, die Konstruktorinjektion
  • Verwenden Sie für optionale oder änderbare Abhängigkeiten die Setter-Injektion
  • Vermeiden Sie in den meisten Fällen eine Feldinjektion

Nachteile der Feldinjektion

Die Gründe, warum die Feldinjektion verpönt ist, sind folgende:

  • Sie können keine unveränderlichen Objekte erstellen, wie dies bei der Konstruktorinjektion der Fall ist
  • Ihre Klassen sind eng mit Ihrem DI-Container verbunden und können nicht außerhalb des Containers verwendet werden
  • Ihre Klassen können nicht ohne Reflexion instanziiert werden (z. B. in Unit-Tests). Sie benötigen den DI-Container, um sie zu instanziieren, wodurch Ihre Tests eher Integrationstests ähneln
  • Ihre realen Abhängigkeiten sind von außen verborgen und spiegeln sich nicht in Ihrer Schnittstelle wider (weder Konstruktoren noch Methoden).
  • Es ist wirklich einfach, zehn Abhängigkeiten zu haben. Wenn Sie die Konstruktorinjektion verwenden würden, hätten Sie einen Konstruktor mit zehn Argumenten, die signalisieren würden, dass etwas faul ist. Sie können injizierte Felder jedoch unbegrenzt mit der Feldinjektion hinzufügen. Zu viele Abhängigkeiten sind eine rote Fahne dafür, dass die Klasse normalerweise mehr als eine Sache tut und dass sie möglicherweise gegen das Prinzip der Einzelverantwortung verstößt.

Fazit

Abhängig von Ihren Anforderungen sollten Sie hauptsächlich die Konstruktorinjektion oder eine Mischung aus Konstruktor- und Setterinjektion verwenden. Die Feldinjektion hat viele Nachteile und sollte vermieden werden. Der einzige Vorteil der Feldinjektion besteht darin, dass das Schreiben bequemer ist, was nicht alle Nachteile überwiegt.


Weiterführende Literatur

Ich habe einen Blog-Artikel darüber geschrieben, warum Feldinjektion normalerweise nicht empfohlen wird: Feldabhängigkeitsinjektion wird als schädlich angesehen .

Vojtech Ruzicka
quelle
12
Es ist im Allgemeinen keine gute Idee und nicht schön, der Welt zu sagen, "Feldinjektion sollte vermieden werden". Zeigen Sie Profis und Kontras und lassen Sie andere selbst entscheiden;) Viele Menschen haben andere Erfahrungen und eigene Sichtweisen.
Dieter
6
Das mag hier der Fall sein, aber es gibt andere Fälle, in denen die Gemeinschaft zu einem allgemeinen Konsens gekommen ist, um etwas zu entmutigen. Nehmen Sie zum Beispiel die ungarische Notation.
Jannik
Sie geben einige gute Punkte als Testbarkeit und Abhängigkeitssichtbarkeit an, aber ich bin nicht mit allen einverstanden. Konstruktorinjektion hat keine Nachteile? Es kann wünschenswert sein, 5 oder 6 Felder in der Klasse zu injizieren, die echte Anrufzusammensetzungen ausführen. Ich bin auch nicht einverstanden mit Ihnen mit Unveränderlichkeit. Endgültige Felder sind nicht zwingend erforderlich, um eine Klasse unveränderlich zu haben. Es ist zu bevorzugen. Welches ist sehr unterschiedlich.
Davidxxx
Ich denke, Sie meinten "Für obligatorische Abhängigkeiten oder wenn Sie Unveränderlichkeit anstreben "
Alex Terreaux
1
Ich bezog mich auf den Link am Anfang der Antwort, der auf die Frühlingsdokumente verweist
Vojtech Ruzicka
47

Dies ist eine der unendlichen Diskussionen in der Softwareentwicklung, aber die wichtigsten Einflussfaktoren in der Branche werden zunehmend zu dem Thema aufgefordert und schlagen vor, die Konstruktorinjektion als bessere Option vorzuschlagen.

Konstruktorinjektion

Vorteile:

  • Bessere Testbarkeit . In Unit-Tests benötigen Sie keine Spottbibliothek oder einen Spring-Kontext. Sie können ein Objekt erstellen, das Sie mit dem neuen Schlüsselwort testen möchten . Solche Tests sind immer schneller, weil sie nicht auf dem Reflexionsmechanismus beruhen. ( Diese Frage wurde 30 Minuten später gestellt. Wenn der Autor die Konstruktorinjektion verwendet hätte, wäre sie nicht erschienen.)
  • Unveränderlichkeit . Sobald die Abhängigkeiten festgelegt sind, können sie nicht mehr geändert werden.
  • Sichererer Code . Nach der Ausführung eines Konstruktors ist Ihr Objekt einsatzbereit, da Sie alles überprüfen können, was als Parameter übergeben wurde. Das Objekt kann entweder bereit sein oder nicht, es liegt kein Zustand dazwischen. Mit der Feldinjektion führen Sie einen Zwischenschritt ein, wenn das Objekt zerbrechlich ist.
  • Saubererer Ausdruck obligatorischer Abhängigkeiten . Die Feldinjektion ist in dieser Angelegenheit nicht eindeutig.
  • Lässt Entwickler über das Design nachdenken . dit schrieb über einen Konstruktor mit 8 Parametern, was eigentlich ein Zeichen für ein schlechtes Design und das Anti-Muster des Gott-Objekts ist . Es spielt keine Rolle, ob eine Klasse 8 Abhängigkeiten in ihrem Konstruktor oder in Feldern hat, es ist immer falsch. Die Leute zögern eher, einem Konstruktor mehr Abhängigkeiten hinzuzufügen als über Felder. Es ist ein Signal an Ihr Gehirn, dass Sie eine Weile innehalten und über Ihre Codestruktur nachdenken sollten.

Nachteile:

  • Mehr Code (aber moderne IDEs lindern den Schmerz).

Grundsätzlich ist die Feldinjektion das Gegenteil.

Daniel Olszewski
quelle
1
Testbarkeit, ja, es war ein Albtraum für mich, die Bohnen mit Feldinjektion zu verspotten. Einmal habe ich die Contsructor-Injektion verwendet, ich muss keine unnötigen Verspottungen machen
Kenobiwan
25

Geschmackssache. Es ist deine Entscheidung.

Aber ich kann erklären, warum ich niemals Konstruktorinjektion verwende .

  1. Ich möchte nicht einen Konstruktor für alle meine implementieren @Service, @Repositoryund @ControllerBohnen. Ich meine, es gibt ungefähr 40-50 Bohnen oder mehr. Jedes Mal, wenn ich ein neues Feld hinzufüge, muss ich den Konstruktor erweitern. Nein, ich will es nicht und ich muss nicht.

  2. Was ist, wenn für Ihre Bean (Service oder Controller) viele andere Beans injiziert werden müssen? Ein Konstruktor mit 4+ Parametern ist sehr hässlich.

  3. Wenn ich CDI verwende, geht mich der Konstruktor nichts an.


EDIT # 1 : Vojtech Ruzicka sagte:

Klasse hat zu viele Abhängigkeiten und verstößt wahrscheinlich gegen das Prinzip der Einzelverantwortung und sollte überarbeitet werden

Ja. Theorie und Realität. Hier ist ein Beispiel: DashboardControllereinem einzelnen Pfad zugeordnet *:8080/dashboard.

My DashboardControllersammelt viele Informationen von anderen Diensten, um sie auf einer Dashboard- / Systemübersichtsseite anzuzeigen. Ich brauche diesen einzelnen Controller. Ich muss also nur diesen einen Pfad sichern (Basisauthentifizierung oder Benutzerrollenfilter).

EDIT # 2 : Da sich jeder auf die 8 Parameter im Konstruktor konzentriert ... Dies war ein reales Beispiel - ein Legacy-Code des Kunden. Ich habe das geändert. Die gleiche Argumentation gilt für mich für 4+ Parameter.

Es geht nur um Code-Injection, nicht um Instanzkonstruktion.

Dieter
quelle
34
Ein sehr hässlicher Konstruktor mit 8 Abhängigkeiten ist wirklich fantastisch, da es eine rote Fahne ist, dass etwas nicht stimmt, die Klasse zu viele Abhängigkeiten hat und wahrscheinlich gegen das Prinzip der Einzelverantwortung verstößt und überarbeitet werden sollte. Es ist eigentlich eine gute Sache.
Vojtech Ruzicka
6
@VojtechRuzicka es ist sicher nicht schön, aber manchmal kann man es nicht vermeiden.
Dieter
4
Ich würde sagen, eine Faustregel von 3, geschweige denn 40-50, Abhängigkeiten für jede Klasse sollten ein Zeichen dafür sein, dass Sie umgestalten müssen. Es gibt keine Möglichkeit, dass eine Klasse mit 40 Abhängigkeiten am Prinzip der Einzelverantwortung oder am Prinzip des Öffnens / Schließens festhält.
Amin J
4
@AminJ Die Regel ist großartig, aber die Realität sieht anders aus. Das Unternehmen, in dem ich arbeite, ist über 20 Jahre alt und wir haben viel Legacy-Code. Refactoring ist eine gute Idee, kostet aber Geld. Ich weiß auch nicht, warum ich es sage, aber ich meinte nicht 40-50 Abhängigkeiten, ich meine 40-50 Bohnen, Komponenten, Module ...
Dieter
7
@dit, Ihre Situation ist eindeutig eine, in der technische Schulden dazu führen, dass Sie nicht optimale Entscheidungen treffen. Nach Ihren eigenen Worten befinden Sie sich in einer Situation, in der Ihre Entscheidungsfindung maßgeblich von Legacy-Code beeinflusst wird, der älter als 20 Jahre ist. Würden Sie zu Beginn eines neuen Projekts weiterhin eine Feldinjektion anstelle einer Konstruktorinjektion empfehlen? Vielleicht sollten Sie in Ihrer Antwort eine Einschränkung einfügen, um anzugeben, in welchen Fällen Sie die Feldinjektion wählen würden.
Umar Farooq Khawaja
0

Noch ein Kommentar - Vojtech Ruzicka erklärte, dass Spring Bohnen auf drei Arten injiziert (die Antwort mit der größten Anzahl von Punkten):

  1. Durch einen Konstruktor
  2. Durch Setter oder andere Methoden
  3. Durch Reflexion direkt in Felder

Diese Antwort ist FALSCH - denn FÜR JEDE ART VON INJEKTIONSFEDER VERWENDET REFLEXION! Verwenden Sie IDE, setzen Sie den Haltepunkt für Setter / Konstruktor und überprüfen Sie.

Dies kann Geschmackssache sein, aber auch FALL. @dieter lieferte einen hervorragenden Fall, wenn die Feldinjektion besser ist. Wenn Sie die Feldinjektion in Integrationstests verwenden, die den Spring-Kontext einrichten, ist das Argument mit der Testbarkeit der Klasse ebenfalls ungültig, es sei denn, Sie möchten später Tests in Ihre Integrationstests schreiben;)

wujek.oczko
quelle