Ich stellte eine allgemeine Spring-Frage: Spring Beans automatisch gegossen und mehrere Personen antworteten, dass das Anrufen von Spring's ApplicationContext.getBean()
so weit wie möglich vermieden werden sollte. Warum ist das so?
Wie kann ich sonst auf die Beans zugreifen, für deren Erstellung ich Spring konfiguriert habe?
Ich verwende Spring in einer Nicht-Webanwendung und hatte geplant, auf ein freigegebenes ApplicationContext
Objekt zuzugreifen, wie von LiorH beschrieben .
Änderung
Ich akzeptiere die Antwort unten, aber hier ist eine alternative Einstellung von Martin Fowler, der die Vorzüge der Abhängigkeitsinjektion im Vergleich zur Verwendung eines Service Locator (der im Wesentlichen dem Aufrufen eines Wraps entspricht ApplicationContext.getBean()
) erläutert .
Zum Teil stellt Fowler fest: "Beim Service Locator fordert die Anwendungsklasse [den Service] explizit durch eine Nachricht an den Locator an. Bei der Injektion erfolgt keine explizite Anforderung, der Service wird in der Anwendungsklasse angezeigt - daher die Umkehrung der Steuerung." Die Inversion der Steuerung ist ein häufiges Merkmal von Frameworks, hat jedoch ihren Preis. Sie ist in der Regel schwer zu verstehen und führt zu Problemen beim Debuggen. Im Großen und Ganzen vermeide ich sie daher [Inversion der Steuerung ] es sei denn, ich brauche es. Das soll nicht heißen, dass es eine schlechte Sache ist, nur dass ich denke, es muss sich über die einfachere Alternative rechtfertigen. "
new MyOtherClass()
? Ich weiß über @Autowired Bescheid, aber ich habe es immer nur auf Feldern verwendet und es bricht weiternew MyOtherClass()
.applicationContext.getBean
ist keine Abhängigkeitsinjektion: Es greift direkt auf das Framework zu und verwendet es als Service Locator .Provider<Foo>
anstelle von a injizierenFoo
undprovider.get()
jedes Mal aufrufen, wenn Sie ein benötigen neue Instanz. Kein Verweis auf den Container selbst, und Sie können problemlos einenProvider
zum Testen erstellen .Gründe, Service Locator der Inversion of Control (IoC) vorzuziehen, sind:
Service Locator ist für andere Personen viel einfacher zu befolgen. IoC ist 'magisch', aber Wartungsprogrammierer müssen Ihre verschlungenen Federkonfigurationen und die Vielzahl von Orten verstehen, um herauszufinden, wie Sie Ihre Objekte verkabelt haben.
IoC ist schrecklich für das Debuggen von Konfigurationsproblemen. In bestimmten Anwendungsklassen wird die Anwendung nicht gestartet, wenn sie falsch konfiguriert ist, und Sie haben möglicherweise keine Möglichkeit, die Vorgänge mit einem Debugger zu durchlaufen.
IoC basiert hauptsächlich auf XML (Anmerkungen verbessern die Dinge, aber es gibt immer noch viel XML). Das bedeutet, dass Entwickler nur dann an Ihrem Programm arbeiten können, wenn sie alle von Spring definierten Magic Tags kennen. Es ist nicht mehr gut genug, Java zu kennen. Dies behindert weniger erfahrene Programmierer (dh es ist eigentlich ein schlechtes Design, eine kompliziertere Lösung zu verwenden, wenn eine einfachere Lösung wie Service Locator dieselben Anforderungen erfüllt). Außerdem ist die Unterstützung für die Diagnose von XML-Problemen weitaus schwächer als die Unterstützung für Java-Probleme.
Die Abhängigkeitsinjektion eignet sich eher für größere Programme. Meistens lohnt sich die zusätzliche Komplexität nicht.
Oft wird Spring verwendet, wenn Sie "die Implementierung später ändern möchten". Es gibt andere Möglichkeiten, dies ohne die Komplexität von Spring IoC zu erreichen.
Bei Webanwendungen (Java EE WARs) wird der Spring-Kontext zur Kompilierungszeit effektiv gebunden (es sei denn, Sie möchten, dass Operatoren den Kontext im Explosionskrieg durchsuchen). Sie können Spring dazu bringen, Eigenschaftendateien zu verwenden. Bei Servlets müssen sich die Eigenschaftendateien jedoch an einem festgelegten Speicherort befinden. Dies bedeutet, dass Sie nicht mehrere Servlets gleichzeitig auf derselben Box bereitstellen können. Sie können Spring mit JNDI verwenden, um die Eigenschaften beim Servlet-Start zu ändern. Wenn Sie jedoch JNDI für vom Administrator veränderbare Parameter verwenden, verringert sich der Bedarf an Spring selbst (da JNDI effektiv ein Service Locator ist).
Mit Spring können Sie die Programmsteuerung verlieren, wenn Spring an Ihre Methoden sendet. Dies ist praktisch und funktioniert für viele Arten von Anwendungen, jedoch nicht für alle. Möglicherweise müssen Sie den Programmfluss steuern, wenn Sie während der Initialisierung Aufgaben (Threads usw.) erstellen müssen oder modifizierbare Ressourcen benötigen, von denen Spring nicht wusste, wann der Inhalt an Ihre WAR gebunden war.
Der Frühling ist sehr gut für das Transaktionsmanagement und hat einige Vorteile. Es ist nur so, dass IoC in vielen Situationen überentwickelt sein und ungerechtfertigte Komplexität für die Betreuer mit sich bringen kann. Verwenden Sie IoC nicht automatisch, ohne vorher darüber nachzudenken, wie Sie es nicht verwenden können.
quelle
Es ist wahr, dass durch die Aufnahme der Klasse in application-context.xml die Verwendung von getBean vermieden wird. Aber auch das ist eigentlich unnötig. Wenn Sie eine eigenständige Anwendung schreiben und Ihre Treiberklasse NICHT in application-context.xml aufnehmen möchten, können Sie den folgenden Code verwenden, damit Spring die Abhängigkeiten des Treibers automatisch verdrahtet:
Ich musste dies einige Male tun, wenn ich eine eigenständige Klasse habe, die einen Aspekt meiner App verwenden muss (z. B. zum Testen), aber ich möchte sie nicht in den Anwendungskontext aufnehmen, da dies nicht der Fall ist eigentlich Teil der App. Beachten Sie auch, dass dadurch vermieden wird, dass die Bean mit einem String-Namen nachgeschlagen werden muss, was ich immer für hässlich gehalten habe.
quelle
@Autowired
Annotation erfolgreich anwenden.Einer der coolsten Vorteile von Spring ist, dass Sie Ihre Objekte nicht miteinander verbinden müssen. Zeus 'Kopf spaltet sich auf und Ihre Klassen erscheinen vollständig geformt mit all ihren Abhängigkeiten, die nach Bedarf erstellt und verkabelt wurden. Es ist magisch und fantastisch.
Je mehr du sagst
ClassINeed classINeed = (ClassINeed)ApplicationContext.getBean("classINeed");
, desto weniger Magie bekommst du. Weniger Code ist fast immer besser. Wenn Ihre Klasse wirklich eine ClassINeed-Bohne brauchte, warum haben Sie sie nicht einfach angeschlossen?Das heißt, etwas muss offensichtlich das erste Objekt erstellen. Es ist nichts Falsches daran, dass Ihre Hauptmethode ein oder zwei Bohnen über getBean () erwirbt, aber Sie sollten dies vermeiden, da Sie bei jeder Verwendung nicht wirklich die gesamte Magie des Frühlings nutzen.
quelle
Die Motivation besteht darin, Code zu schreiben, der nicht explizit von Spring abhängt. Auf diese Weise müssen Sie beim Wechseln von Containern keinen Code neu schreiben.
Stellen Sie sich den Container als etwas vor, das für Ihren Code unsichtbar ist und auf magische Weise für seine Anforderungen sorgt, ohne gefragt zu werden.
Die Abhängigkeitsinjektion ist ein Kontrapunkt zum Muster "Service Locator". Wenn Sie Abhängigkeiten nach Namen suchen möchten, können Sie auch den DI-Container entfernen und so etwas wie JNDI verwenden.
quelle
Verwenden
@Autowired
oderApplicationContext.getBean()
ist wirklich das gleiche. Auf beide Arten erhalten Sie die Bean, die in Ihrem Kontext konfiguriert ist, und auf beide Arten hängt Ihr Code von spring ab. Das einzige, was Sie vermeiden sollten, ist das Instanziieren Ihres ApplicationContext. Mach das nur einmal! Mit anderen Worten, eine Linie wiesollte nur einmal in Ihrer Anwendung verwendet werden.
quelle
Die Idee ist, dass Sie sich auf die Abhängigkeitsinjektion ( Inversion of Control oder IoC) verlassen. Das heißt, Ihre Komponenten werden mit den Komponenten konfiguriert, die sie benötigen. Diese Abhängigkeiten werden injiziert (über den Konstruktor oder die Setter) - Sie erhalten sie dann nicht selbst.
ApplicationContext.getBean()
erfordert, dass Sie eine Bean explizit in Ihrer Komponente benennen. Stattdessen kann Ihre Konfiguration mithilfe von IoC bestimmen, welche Komponente verwendet wird.Auf diese Weise können Sie Ihre Anwendung mit verschiedenen Komponentenimplementierungen einfach neu verkabeln oder Objekte zum Testen auf einfache Weise konfigurieren, indem Sie verspottete Varianten bereitstellen (z. B. ein verspottetes DAO, damit Sie beim Testen nicht auf eine Datenbank stoßen).
quelle
Andere haben auf das allgemeine Problem hingewiesen (und sind gültige Antworten), aber ich werde nur einen zusätzlichen Kommentar abgeben: Es ist nicht so, dass Sie es NIEMALS tun sollten, sondern dass Sie es so wenig wie möglich tun.
Normalerweise bedeutet dies, dass es genau einmal gemacht wird: während des Bootstrappens. Und dann müssen Sie nur noch auf die "Root" -Bohne zugreifen, über die andere Abhängigkeiten aufgelöst werden können. Dies kann wiederverwendbarer Code sein, wie z. B. ein Basisservlet (wenn Web-Apps entwickelt werden).
quelle
Eine der Prämissen von Spring ist die Vermeidung von Kopplungen . Definieren und verwenden Sie Interfaces, DI, AOP und vermeiden Sie die Verwendung von ApplicationContext.getBean () :-)
quelle
Einer der Gründe ist die Testbarkeit. Angenommen, Sie haben diese Klasse:
Wie können Sie diese Bohne testen? ZB so:
Einfach richtig?
Während Sie (aufgrund der Anmerkungen) immer noch von Spring abhängig sind, können Sie Ihre Abhängigkeit von Spring entfernen, ohne Code zu ändern (nur die Anmerkungsdefinitionen), und der Testentwickler muss nichts darüber wissen, wie Spring funktioniert (vielleicht sollte er es trotzdem, aber Es ermöglicht die Überprüfung und Prüfung des Codes getrennt von den Funktionen der Feder.
Bei Verwendung des ApplicationContext ist dies weiterhin möglich. Dann müssen Sie sich jedoch lustig machen,
ApplicationContext
was eine riesige Schnittstelle ist. Sie benötigen entweder eine Dummy-Implementierung oder Sie können ein Verspottungsframework wie Mockito verwenden:Dies ist eine ziemliche Möglichkeit, aber ich denke, die meisten Leute würden zustimmen, dass die erste Option eleganter ist und den Test einfacher macht.
Die einzige Option, die wirklich ein Problem darstellt, ist diese:
Das Testen erfordert große Anstrengungen, oder Ihre Bean wird bei jedem Test versuchen, eine Verbindung zum Stackoverflow herzustellen. Und sobald Sie einen Netzwerkfehler haben (oder die Administratoren bei Stackoverflow Sie aufgrund einer übermäßigen Zugriffsrate blockieren), werden Sie zufällig fehlgeschlagene Tests haben.
Abschließend würde ich nicht sagen, dass die
ApplicationContext
direkte Verwendung automatisch falsch ist und unter allen Umständen vermieden werden sollte. Wenn es jedoch bessere Optionen gibt (und in den meisten Fällen auch), verwenden Sie die besseren Optionen.quelle
Ich habe nur zwei Situationen gefunden, in denen getBean () erforderlich war:
Andere haben erwähnt, dass getBean () in main () verwendet wird, um die "main" -Bohne für ein eigenständiges Programm abzurufen.
Eine andere Verwendung, die ich mit getBean () gemacht habe, sind Situationen, in denen eine interaktive Benutzerkonfiguration das Bean-Make-up für eine bestimmte Situation bestimmt. So durchläuft beispielsweise ein Teil des Boot-Systems eine Datenbanktabelle mit getBean () mit einer Bean-Definition von scope = 'prototype' und legt dann zusätzliche Eigenschaften fest. Vermutlich gibt es eine Benutzeroberfläche, die die Datenbanktabelle anpasst, die freundlicher wäre als der Versuch, das XML des Anwendungskontexts (neu) zu schreiben.
quelle
Es gibt eine andere Zeit, in der die Verwendung von getBean sinnvoll ist. Wenn Sie ein bereits vorhandenes System neu konfigurieren, bei dem die Abhängigkeiten in Spring-Kontextdateien nicht explizit aufgerufen werden. Sie können den Prozess starten, indem Sie getBean aufrufen, damit Sie nicht alles auf einmal verkabeln müssen. Auf diese Weise können Sie langsam Ihre Federkonfiguration aufbauen, indem Sie jedes Teil im Laufe der Zeit in Position bringen und die Bits richtig ausrichten. Die Aufrufe von getBean werden irgendwann ersetzt, aber wenn Sie die Struktur des Codes verstehen oder nicht, können Sie damit beginnen, mehr und mehr Beans zu verkabeln und immer weniger Aufrufe für getBean zu verwenden.
quelle
Es gibt jedoch immer noch Fälle, in denen Sie das Service Locator-Muster benötigen. Zum Beispiel habe ich eine Controller-Bean. Dieser Controller verfügt möglicherweise über einige Standard-Service-Beans, deren Abhängigkeit durch die Konfiguration injiziert werden kann. Es kann jedoch auch viele zusätzliche oder neue Dienste geben, die dieser Controller jetzt oder später aufrufen kann. Diese benötigen dann den Service Locator, um die Service Beans abzurufen.
quelle
Sie sollten Folgendes verwenden: ConfigurableApplicationContext anstelle von ApplicationContext
quelle