313 Stimmen

Warum wird ApplicationContext.getBean von Spring als schlecht angesehen?

Ich habe eine allgemeine Frage zum Thema Frühling gestellt: Automatisch gegossene Frühlingsbohnen und mehrere Leute haben geantwortet, dass der Aufruf von Spring ApplicationContext.getBean() sollten so weit wie möglich vermieden werden. Warum ist das so?

Wie sollte ich sonst Zugriff auf die Beans erhalten, die ich mit Spring erstellt habe?

Ich verwende Spring in einer Nicht-Web-Anwendung und hatte geplant, den Zugriff auf eine freigegebene ApplicationContext Objekt wie von LiorH beschrieben .

Abänderung

Ich akzeptiere die unten stehende Antwort, aber hier ist eine andere Meinung von Martin Fowler, der erörtert die Vorzüge von Dependency Injection gegenüber der Verwendung eines Service Locators (was im Wesentlichen dasselbe ist wie der Aufruf einer umhüllten ApplicationContext.getBean() ).

Fowler erklärt unter anderem: " Beim Service Locator fordert die Anwendungsklasse den Dienst explizit durch eine Nachricht an den Locator an. Bei der Injektion gibt es keine explizite Anfrage, der Dienst erscheint in der Anwendungsklasse - daher die Umkehrung der Kontrolle. Die Umkehrung der Kontrolle ist ein gängiges Merkmal von Frameworks, aber sie hat ihren Preis. Sie ist in der Regel schwer zu verstehen und führt zu Problemen bei der Fehlersuche. Im Großen und Ganzen ziehe ich es also vor, sie [die Umkehrung der Kontrolle] zu vermeiden, wenn ich sie nicht brauche. Das soll nicht heißen, dass es etwas Schlechtes ist, ich denke nur, dass es sich gegenüber der einfacheren Alternative rechtfertigen muss. "

223voto

ColinD Punkte 106101

Ich habe dies in einem Kommentar zu einer anderen Frage erwähnt, aber die ganze Idee der Umkehrung der Kontrolle besteht darin, dass man keine Ihrer Klassen weiß oder kümmert sich darum, wie sie die Objekte erhält, von denen sie abhängt . Dadurch ist es einfach, die Art der Implementierung einer bestimmten Abhängigkeit jederzeit zu ändern. Außerdem lassen sich die Klassen so leicht testen, da Sie Scheinimplementierungen von Abhängigkeiten bereitstellen können. Schließlich macht es die Klassen Einfacher und sich mehr auf ihre Kernaufgaben konzentrieren.

通話について ApplicationContext.getBean() ist keine Inversion der Kontrolle! Es ist zwar immer noch einfach zu ändern, welche Implementierung für den gegebenen Bean-Namen konfiguriert ist, aber die Klasse verlässt sich jetzt direkt auf Spring, um diese Abhängigkeit bereitzustellen, und kann sie nicht auf andere Weise erhalten. Sie können nicht einfach Ihre eigene Mock-Implementierung in einer Testklasse erstellen und diese selbst an die Klasse übergeben. Dadurch wird der Zweck von Spring als Container für die Injektion von Abhängigkeiten im Grunde zunichte gemacht.

Überall, wo Sie sagen wollen:

MyClass myClass = applicationContext.getBean("myClass");

sollten Sie stattdessen z.B. eine Methode deklarieren:

public void setMyClass(MyClass myClass) {
   this.myClass = myClass;
}

Und dann in Ihrer Konfiguration:

<bean id="myClass" class="MyClass">...</bean>

<bean id="myOtherClass" class="MyOtherClass">
   <property name="myClass" ref="myClass"/>
</bean>

Spring injiziert dann automatisch myClass in myOtherClass .

Erklären Sie alles auf diese Weise, und am Ende steht dann etwas wie:

<bean id="myApplication" class="MyApplication">
   <property name="myCentralClass" ref="myCentralClass"/>
   <property name="myOtherCentralClass" ref="myOtherCentralClass"/>
</bean>

MyApplication ist die zentralste Klasse und hängt zumindest indirekt von jedem anderen Dienst in Ihrem Programm ab. Beim Bootstrapping, in Ihrem main Methode, können Sie applicationContext.getBean("myApplication") aber Sie sollten es nicht nötig haben, die getBean() irgendwo anders!

67voto

Moa Punkte 739

Folgende Gründe sprechen dafür, Service Locator gegenüber Inversion of Control (IoC) vorzuziehen:

  1. Mit Service Locator ist es für andere viel, viel einfacher, Ihrem Code zu folgen. IoC ist "magisch", aber Wartungsprogrammierer müssen Ihre verworrenen Spring-Konfigurationen und all die unzähligen Speicherorte verstehen, um herauszufinden, wie Sie Ihre Objekte verdrahtet haben.

  2. IoC eignet sich hervorragend zum Debuggen von Konfigurationsproblemen. Bei bestimmten Anwendungsklassen startet die Anwendung nicht, wenn sie falsch konfiguriert ist, und Sie haben möglicherweise keine Möglichkeit, die Vorgänge mit einem Debugger zu analysieren.

  3. IoC ist in erster Linie XML-basiert (Annotationen verbessern die Situation, aber es gibt immer noch eine Menge XML). Das bedeutet, dass Entwickler nur dann an Ihrem Programm arbeiten können, wenn sie alle magischen Tags kennen, die von Spring definiert werden. Es reicht nicht mehr aus, Java zu kennen. Dies behindert weniger erfahrene Programmierer (d. h. es ist eigentlich ein schlechtes Design, eine kompliziertere Lösung zu verwenden, wenn eine einfachere Lösung, wie z. B. Service Locator, die gleichen Anforderungen erfüllen kann). Außerdem ist die Unterstützung für die Diagnose von XML-Problemen viel schwächer als die Unterstützung für Java-Probleme.

  4. Dependency Injection ist eher für größere Programme geeignet. In den meisten Fällen lohnt sich die zusätzliche Komplexität nicht.

  5. Oft wird Spring für den Fall verwendet, dass man "die Implementierung später ändern möchte". Es gibt andere Möglichkeiten, dies ohne die Komplexität von Spring IoC zu erreichen.

  6. Bei Webanwendungen (Java EE WARs) wird der Spring-Kontext effektiv zur Kompilierzeit gebunden (es sei denn, Sie möchten, dass die Operatoren den Kontext im explodierten War umgehen). Sie können Spring dazu bringen, Eigenschaftsdateien zu verwenden, aber bei Servlets müssen sich die Eigenschaftsdateien an einem bestimmten Ort befinden, was bedeutet, dass Sie nicht mehrere Servlets zur gleichen Zeit auf der gleichen Box einsetzen können. Sie können Spring mit JNDI verwenden, um die Eigenschaften beim Starten des Servlets zu ändern, aber wenn Sie JNDI für vom Administrator veränderbare Parameter verwenden, verringert sich die Notwendigkeit für Spring selbst (da JNDI effektiv ein Service Locator ist).

  7. Mit Spring können Sie die Programmkontrolle verlieren, wenn Spring an Ihre Methoden sendet. Das ist praktisch und funktioniert für viele Arten von Anwendungen, aber nicht für alle. Möglicherweise müssen Sie den Programmfluss kontrollieren, wenn Sie während der Initialisierung Aufgaben (Threads usw.) erstellen müssen oder veränderbare Ressourcen benötigen, die Spring nicht kannte, als der Inhalt an Ihre WAR gebunden wurde.

Spring eignet sich sehr gut für das Transaktionsmanagement und hat einige Vorteile. Es ist nur so, dass IoC in vielen Situationen ein Over-Engineering sein kann und ungerechtfertigte Komplexität für Maintainer mit sich bringt. Verwenden Sie nicht automatisch IoC, ohne vorher über Möglichkeiten nachzudenken, es nicht zu verwenden.

25voto

iftheshoefritz Punkte 4933

Es stimmt, dass durch die Aufnahme der Klasse in die Datei application-context.xml die Verwendung von getBean vermieden werden kann. Aber auch das ist eigentlich unnötig. Wenn Sie eine eigenständige Anwendung schreiben und Ihre Treiberklasse NICHT in die application-context.xml aufnehmen möchten, können Sie den folgenden Code verwenden, damit Spring die Abhängigkeiten des Treibers automatisch verknüpft:

public class AutowireThisDriver {

    private MySpringBean mySpringBean;    

    public static void main(String[] args) {
       AutowireThisDriver atd = new AutowireThisDriver(); //get instance

       ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
                  "/WEB-INF/applicationContext.xml"); //get Spring context 

       //the magic: auto-wire the instance with all its dependencies:
       ctx.getAutowireCapableBeanFactory().autowireBeanProperties(atd,
                  AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);        

       // code that uses mySpringBean ...
       mySpringBean.doStuff() // no need to instantiate - thanks to Spring
    }

    public void setMySpringBean(MySpringBean bean) {
       this.mySpringBean = bean;    
    }
}

Ich habe benötigt, um dies ein paar Mal zu tun, wenn ich eine Art von Standalone-Klasse, die irgendeinen Aspekt meiner app (z. B. zum Testen) verwenden muss, aber ich möchte nicht es in application-context enthalten, weil es nicht tatsächlich Teil der app ist. Beachten Sie auch, dass dies die Notwendigkeit vermeidet, die Bean mit einem String-Namen zu suchen, was ich immer für hässlich gehalten habe.

23voto

Brandon Yarbrough Punkte 34849

Einer der größten Vorteile der Verwendung von Spring ist, dass Sie Ihre Objekte nicht miteinander verdrahten müssen. Der Kopf von Zeus spaltet sich auf und Ihre Klassen erscheinen, vollständig geformt, mit all ihren Abhängigkeiten erstellt und verdrahtet, wie benötigt. Das ist magisch und fantastisch.

Je mehr Sie sagen ClassINeed classINeed = (ClassINeed)ApplicationContext.getBean("classINeed"); desto weniger Magie erhalten Sie. Weniger Code ist fast immer besser. Wenn Ihre Klasse wirklich eine ClassINeed-Bohne benötigt, warum haben Sie sie nicht einfach eingebaut?

Allerdings muss das erste Objekt natürlich irgendwie erstellt werden. Es ist nichts dagegen einzuwenden, wenn Ihre Hauptmethode eine oder zwei Beans über getBean() erwirbt, aber Sie sollten es vermeiden, denn wenn Sie es verwenden, nutzen Sie nicht wirklich die ganze Magie von Spring.

16voto

erickson Punkte 256579

Die Motivation ist, Code zu schreiben, der nicht explizit von Spring abhängt. Auf diese Weise müssen Sie, wenn Sie den Container wechseln, 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 Bedürfnisse sorgt, ohne dass er darum gebeten wird.

Dependency Injection ist ein Gegenpol zum "Service Locator"-Muster. Wenn Sie Abhängigkeiten namentlich nachschlagen wollen, können Sie den DI-Container ebenso gut abschaffen und etwas wie JNDI verwenden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X