Warum ist der Frühling die Datei ApplicationContext.getBean als schlecht angesehen?

StackOverflow https://stackoverflow.com/questions/812415

  •  03-07-2019
  •  | 
  •  

Frage

Ich fragte eine Allgemeine Frühjahr Frage: Auto-cast Spring Beans und hatte mehrere Menschen darauf reagieren, dass der Aufruf Spring ApplicationContext.getBean() sollte vermieden werden so viel wie möglich.Warum ist das so?

Wie sollte ich sonst Zugriff auf die Bohnen habe ich konfiguriert Frühling zu erstellen?

Ich bin mit Spring in eine nicht-web-Anwendung und hatte geplant, den Zugriff auf einen freigegebenen ApplicationContext Objekt wie von LiorH.

Änderung

Ich akzeptiere die Antwort unten, aber hier ist ein alternate take von Martin Fowler, der beschreibt die Vorteile von Dependency Injection vs.über ein Service-Locator (was im wesentlichen das gleiche wie der Aufruf eines eingewickelt ApplicationContext.getBean()).

In Teil Fowler Staaten, "Mit service-locator-Anwendung Klasse fragt ihn [den Dienst] explizit durch eine Nachricht an den locator.Mit der Injektion gibt es keine ausdrücklichen Wunsch, der Dienst wird in der application-Klasse - also der inversion of control.Inversion of control ist ein gemeinsames Merkmal des frameworks, aber es ist etwas, kommt zu einem Preis.Es neigt dazu, schwer zu verstehen und führt zu Problemen, wenn Sie versuchen zu Debuggen.Also auf das ganze ich es vorziehen, es zu vermeiden [Inversion of Control], es sei denn, ich brauche es.Dies ist nicht zu sagen, es ist eine schlechte Sache, nur, dass ich denke, dass es sich rechtfertigen muss über die natürlichere alternative."

War es hilfreich?

Lösung

Ich erwähnte dies in einem Kommentar zu der anderen Frage, aber die ganze Idee der Inversion of Control ist zu haben, keine Ihrer Klassen weiß oder Pflege, wie sie die Objekte, die sie auf abhängen erhalten. Dies macht es leicht, was eine gegebene Abhängigkeit Art der Implementierung zu ändern, die Sie jederzeit nutzen. Es macht auch die Klassen einfach zu testen, wie Sie Mock-Implementierungen von Abhängigkeiten zur Verfügung stellen können. Schließlich macht es die Klassen einfacher und stärker auf ihre Kernaufgabe.

ApplicationContext.getBean() Aufruf ist nicht Inversion of Control! Während es immer noch leicht zu ändern ist, was Implemenation für die gegebenen Bean Namen konfiguriert ist, stützt sich die Klasse jetzt direkt auf Frühling diese Abhängigkeit zu schaffen, und es kann keine andere Art und Weise bekommen. Sie können nicht nur Ihre eigene Mock Implementierung in einer Testklasse machen und dass sie es passieren. Diese im Grunde besiegt Spring Zweck als Dependency Injection Container.

Wo man möchte sagen:

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

Sie sollten stattdessen zum Beispiel erklären, ein Verfahren:

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

Und dann in der Konfiguration:

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

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

Frühling wird dann automatisch myClass in myOtherClass injiziert.

Erklären Sie alles auf diese Weise, und an der Wurzel von allem etwas wie:

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

MyApplication ist die zentrale Klasse, und zumindest mittelbar an jedem anderen Dienst in Ihrem Programm abhängt. Wenn Bootstrapping, in Ihrer main Methode können Sie applicationContext.getBean("myApplication") nennen, aber Sie sollten nicht getBean() anderswo anrufen müssen!

Andere Tipps

Gründe Service Locator über Inversion of Control (IoC) zu bevorzugen sind:

  1. Service Locator ist viel, viel leichter für andere Menschen in Ihrem Code zu folgen. IoC ist ‚Magie‘, sondern Wartung Programmierer müssen Ihre gewundenen Federkonfigurationen und alle die unzähligen Orten verstehen, um herauszufinden, wie Sie Ihre Objekte verdrahtet.

  2. IoC ist schrecklich für Konfigurationsprobleme debuggen. In bestimmten Anwendungsklassen wird die Anwendung nicht gestartet werden, wenn falsch konfiguriert und Sie können nicht eine Chance, um durch das bekommen, was mit einem Debugger vor sich geht.

  3. IoC ist in erster Linie XML-basierte (Annotations verbessern Dinge, aber es gibt immer noch eine Menge XML da draußen). Das bedeutet, dass Entwickler können nicht auf dem Programm arbeiten, es sei denn, sie von Spring definiert alle magischen Tags kennen. Es ist nicht gut genug Java mehr zu wissen. Dies behindert weniger Erfahrung Programmierer (dh. Es wirklich schlechtes Design ist eine kompliziertere Lösung zu verwenden, wenn eine einfachere Lösung, wie Service Locator, wird die gleichen Anforderungen erfüllen). Darüber hinaus Unterstützung für XML-Diagnose von Problemen ist weit schwächer als Unterstützung für Java Probleme.

  4. Dependency Injection ist mehr zu größeren Programmen geeignet. Die meiste Zeit die zusätzliche Komplexität ist es nicht wert.

  5. Oft wird Frühling im Fall verwendet man „vielleicht will später die Umsetzung ändern“. Es gibt andere Wege, dies zu erreichen, ohne die Komplexität von Spring IoC.

  6. Für Web-Anwendungen (Java EE WARs) der Frühling Kontext effektiv bei der Kompilierung gebunden ist (es sei denn, Sie Operatoren zu roden um den Kontext, in dem Explosions Krieg wollen). Sie können Frühlings-Dateien verwenden Eigenschaft, aber mit Servlets Eigenschaftsdateien müssen in einem vorher bestimmten Ort sein, was bedeutet, dass Sie nicht mehrere Servlets der gleichen Zeit auf dem gleichen Feld bereitstellen können. Sie können den Frühling mit JNDI verwenden Eigenschaften bei Servlet Startzeit zu ändern, aber wenn Sie JNDI für Administrator-veränderbare Parameter die Notwendigkeit für den Frühling verwenden verringert sich (da JNDI effektiv ein Service Locator ist).

  7. Mit Frühling können Sie Programmkontrolle verlieren, wenn Frühling auf Ihre Methoden entsendet. Das ist bequem und funktioniert für viele Arten von Anwendungen, aber nicht alle. Möglicherweise müssen Programmablauf steuern, wenn Sie Aufgaben erstellen müssen (Threads usw.) während der Initialisierung oder müssen modifizierbar Ressourcen, die Feder nicht kennen, wenn der Gehalt an Ihren WAR gebunden wurde.

Der Frühling ist sehr gut für das Transaktionsmanagement und hat einige Vorteile. Es ist nur, dass IoC kann in vielen Situationen Over-Engineering und ungerechtfertigten Aufwand für Maintainer einzuführen. Sie IoC nicht automatisch verwenden, ohne von Möglichkeiten der Verwendung von nicht zuerst zu denken.

Es ist wahr, dass darunter auch die Klasse in application-context.xml vermeidet die Notwendigkeit zu verwenden getBean.Aber auch das ist eigentlich unnötig.Wenn Sie schreiben eine standalone-Anwendung, und Sie NICHT möchten, dass Ihre Fahrer in der Klasse application-context.xml Sie können den folgenden code verwenden, um Spring autowire der Fahrer Abhängigkeiten:

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 brauchte, um dies zu tun ein paar mal, wenn ich irgendeine Art von standalone-Klasse verwenden muss, die von einigen Aspekten meiner app (z.B. zum testen) aber ich will nicht, um es in den Anwendungs-Kontext, da ist es eigentlich nicht Teil der app.Beachten Sie auch, dass dies vermeidet die Notwendigkeit zum nachschlagen der bean mit einem String-Namen, die habe ich immer gedacht war hässlich.

Eines der coolsten Vorteile von so etwas wie Frühling ist, dass Sie müssen nicht zusammen Ihre Objekte verdrahten. Zeus Kopf spaltet offen und Ihre Klassen erscheinen, voll mit all ihren Abhängigkeiten geschaffen und verdrahteten in gebildet, je nach Bedarf. Es ist magisch und fantastisch.

Je mehr Sie sagen ClassINeed classINeed = (ClassINeed)ApplicationContext.getBean("classINeed");, desto weniger Magie Sie bekommen. Weniger Code ist fast immer besser. Wenn Ihre Klasse wirklich eine ClassINeed Bohne benötigt, warum hast du nicht verdrahten es nur in?

Das heißt, braucht etwas offensichtlich das erste Objekt zu erstellen. Es ist nichts falsch mit Ihrem Hauptverfahren eine Bohne oder zwei über getBean Erwerb (), aber Sie sollten es vermeiden, weil, wenn Sie es verwenden, sind Sie nicht wirklich alle von der Magie des Frühlings mit.

Die Motivation ist, Code zu schreiben, die nicht explizit auf Frühling abhängen. Auf diese Weise, wenn Sie Container wechseln, müssen Sie keinen Code neu schreiben.

Denken Sie an den Container als etwas, um Ihren Code unsichtbar ist, auf magische Weise für seine Bedürfnisse bereitstellt, ohne gefragt zu werden.

Dependency Injection ist ein Kontrapunkt zur "Service Locator" -Muster. Wenn Sie mit Abhängigkeiten nachzuschlagen Namen gehen, könnten Sie auch loszuwerden, die DI-Container und verwenden Sie so etwas wie JNDI.

Mit @Autowired oder ApplicationContext.getBean() ist wirklich die gleiche Sache. Auf beiden Arten erhalten Sie die Bohne, die in Ihrem Umfeld und in den beiden Möglichkeiten, um Ihren Code auf Feder hängt konfiguriert ist. Das einzige, was Sie vermeiden sollten, ist Instanziieren Ihre Application. Tun Sie dies nur einmal! Mit anderen Worten, eine Zeile wie

ApplicationContext context = new ClassPathXmlApplicationContext("AppContext.xml");

sollte nur einmal in Ihrer Anwendung verwendet werden.

Die Idee ist, dass Sie auf Dependency Injection verlassen ( Inversion of Control oder IoC) . Das heißt, Ihre Komponenten sind so konfiguriert, mit den Komponenten, die sie benötigen. Diese Abhängigkeiten sind injiziert (über den Konstruktor oder Setter.) - bekommt man nicht dann selbst

ApplicationContext.getBean() erfordert, dass Sie in Ihrer Komponente eine Bohne explizit zu nennen. Stattdessen von IoC verwenden, können Sie Ihre Konfiguration bestimmen, welche Komponente verwendet werden.

Auf diese Weise können Sie Ihre Anwendung mit verschiedenen Komponentenimplementierungen leicht verkabeln oder auf einfache Art und Weise Objekte zum Testen konfigurieren, indem Sie verspotteten Varianten bereitstellt (zum Beispiel eines verspottet DAO so treffen Sie keine Datenbank während des Testens)

Andere haben auf das allgemeine Problem hingewiesen (und gültige Antworten), aber ich werde nur einen zusätzlichen Kommentar bieten. Es ist nicht, dass Sie es nie tun sollten, sondern vielmehr, dass es so wenig wie möglich zu tun

In der Regel bedeutet dies, dass es genau einmal getan wird: während Bootstrapping. Und dann ist es einfach die „root“ Bean zuzugreifen, durch die anderen Abhängigkeiten aufgelöst werden können. Dies kann wieder verwendbarer Code, wie Basis-Servlet (wenn Web-Anwendungen entwickeln).

Ein Federraum ist zu vermeiden Kupplung . Definieren und verwenden Schnittstellen, DI, AOP und vermeiden, mit ApplicationContext.getBean (): -)

Einer der Gründe dafür ist die Testbarkeit. Sagen Sie bitte diese Klasse haben:

interface HttpLoader {
    String load(String url);
}
interface StringOutput {
    void print(String txt);
}
@Component
class MyBean {
    @Autowired
    MyBean(HttpLoader loader, StringOutput out) {
        out.print(loader.load("http://stackoverflow.com"));
    }
}

Wie können Sie diese Bohne testen? Z.B. wie folgt aus:

class MyBeanTest {
    public void creatingMyBean_writesStackoverflowPageToOutput() {
        // setup
        String stackOverflowHtml = "dummy";
        StringBuilder result = new StringBuilder();

        // execution
        new MyBean(Collections.singletonMap("https://stackoverflow.com", stackOverflowHtml)::get, result::append);

        // evaluation
        assertEquals(result.toString(), stackOverflowHtml);
    }
}

Einfach, nicht wahr?

Während Sie noch auf Frühling hängen (aufgrund der Anmerkungen) Sie können Sie auf Feder entfernen Abhängigkeits ohne Code zu ändern (nur die Annotation Definitionen) und die Testentwickler braucht nichts darüber, wie Federarbeiten wissen (vielleicht sollte er wie auch immer, aber es erlaubt zu überprüfen und testen sie den Code getrennt von welcher Feder der Fall ist).

Es ist immer noch möglich, das gleiche zu tun, wenn das Application verwenden. Allerdings müssen Sie ApplicationContext verspotten, die eine große Oberfläche ist. Sie müssen entweder eine Dummy-Implementierung oder Sie können einen Mockframework wie Mockito verwenden:

@Component
class MyBean {
    @Autowired
    MyBean(ApplicationContext context) {
        HttpLoader loader = context.getBean(HttpLoader.class);
        StringOutput out = context.getBean(StringOutput.class);

        out.print(loader.load("http://stackoverflow.com"));
    }
}
class MyBeanTest {
    public void creatingMyBean_writesStackoverflowPageToOutput() {
        // setup
        String stackOverflowHtml = "dummy";
        StringBuilder result = new StringBuilder();
        ApplicationContext context = Mockito.mock(ApplicationContext.class);
        Mockito.when(context.getBean(HttpLoader.class))
            .thenReturn(Collections.singletonMap("https://stackoverflow.com", stackOverflowHtml)::get);
        Mockito.when(context.getBean(StringOutput.class)).thenReturn(result::append);

        // execution
        new MyBean(context);

        // evaluation
        assertEquals(result.toString(), stackOverflowHtml);
    }
}

Dies ist eine ganz Möglichkeit, aber ich denke, die meisten Menschen würden zustimmen, dass die erste Option ist eleganter und macht den Test einfacher.

Die einzige Option, die wirklich ein Problem ist, ist diese:

@Component
class MyBean {
    @Autowired
    MyBean(StringOutput out) {
        out.print(new HttpLoader().load("http://stackoverflow.com"));
    }
}

Test erfordert dies große Anstrengungen oder Ihre Bohne wird versuchen, auf jeden Test um eine Verbindung zu Stackoverflow. Und sobald Sie einen Netzwerkfehler (oder die Admins bei Stackoverflow-Block Ihnen aufgrund einer übermäßigen Zugriffsrate) Sie werden nach dem Zufall vorgeschriebenen Prüfungen nicht haben.

So als Abschluss würde ich nicht sagen, dass die ApplicationContext direkt mit automatisch falsch und sollte unter allen Umständen vermieden werden. Wenn es jedoch bessere Möglichkeiten (und es gibt in den meisten Fällen), dann die besseren Optionen.

Ich habe festgestellt, nur zwei Situationen, in denen getBean () erforderlich:

Andere haben mit getBean erwähnt () in main (), um den "main" bean für ein eigenständiges Programm zu holen.

Eine weitere Verwendung von I getBean gemacht habe () ist in Situationen, in denen eine interaktive Benutzerkonfiguration des Bean-Make-up für eine bestimmte Situation bestimmt. So dass zum Beispiel Schleifen Teil des Boot-Systems durch eine Datenbanktabelle getBean () mit einem Umfang unter Verwendung = ‚Prototyp‘ Bean Definition und dann weitere Eigenschaften einstellen. Vermutlich gibt es eine Benutzeroberfläche, die die Datenbanktabelle einstellt, die freundlicher als zu versuchen, (wieder) schreiben den Anwendungskontext XML wäre.

Es gibt eine andere Zeit bei der Verwendung von getBean Sinn macht. Wenn Sie ein System sind neu konfigurieren, die bereits vorhanden ist, wo die Abhängigkeiten nicht explizit im Frühjahr Kontextdateien angezeigt wird. Sie können den Prozess starten, indem Sie in Anrufe zu getBean setzen, so dass Sie auf einmal nicht verkabeln müssen sie alle auf. So können Sie langsam Ihre Federkonfiguration jedes Stück an Ort und Stelle im Laufe der Zeit aufbauen können setzen und die Bits immer gefüttert richtig auf. Die Anrufe getBean wird schließlich ersetzt werden, aber, wie Sie die Struktur des Codes zu verstehen, oder das Fehlen von dort können Sie den Prozess der Verkabelung beginnen mehr und mehr Bohnen und immer weniger Anrufe getBean verwenden.

Allerdings gibt es immer noch Fälle, in denen Sie den Service Locator Muster benötigen. zum Beispiel habe ich eine Controller Bohne, diesen Controller möglicherweise einige Standard-Service-Bohnen haben, die Abhängigkeit von der Konfiguration injiziert werden kann. während es auch jetzt oder später aufrufen können viele zusätzliche oder neue Dienste dieser Controller sein könnte, die dann den Service Locator benötigen die Service-Bohnen zu erhalten.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top