JavaRush /Java-Blog /Random-DE /Frühling. Lektion 2. IoC/DI in der Praxis
Umaralikhon
Level 3
Красноярск

Frühling. Lektion 2. IoC/DI in der Praxis

Veröffentlicht in der Gruppe Random-DE
Und so... In der vorherigen Lektion haben wir kurz den theoretischen Teil von IoC und DI besprochen. Wir haben auch die Konfigurationsdatei pom.xml für unser Projekt eingerichtet. Heute beginnen wir mit der Erstellung des Hauptteils des Programms. Zunächst zeige ich Ihnen, wie Sie ein Programm ohne IoC/DI erstellen. Und dann erstellen wir direkt ein Programm, das selbstständig Abhängigkeiten einführt. Das heißt, die Kontrolle über den Code geht in die Hände des Frameworks über (klingt gruselig). Während wir das Programm verwalten, stellen Sie sich vor, dass es ein bestimmtes Unternehmen gibt. Und das Unternehmen hat (vorerst) zwei Abteilungen: Java-Entwicklung und Einstellungsabteilung. Lassen Sie die Klasse, die die „Java-Entwicklungsabteilung“ beschreibt, über zwei Methoden verfügen: String getName() – Rückgabe des Namens des Mitarbeiters, String getJob() – Rückgabe der Position des Mitarbeiters. (Auflistung 1)
package org.example;

public class JavaDevelopment {

    public String getName(){
        return "Alexa";
    }

    public String getJob(){
        return "Middle Java developer";
    }
}
Lassen Sie die Klasse, die die Einstellungsabteilung beschreibt, einen Eingabekonstruktor haben, der einen Mitarbeiter akzeptiert, und eine void displayInfo()-Methode, die Informationen über Mitarbeiter anzeigt. (Auflistung 2)
package org.example;

public class HiringDepartment {
    private JavaDevelopment javaDevelopment;

    public HiringDepartment(JavaDevelopment javaDevelopment) {
        this.javaDevelopment = javaDevelopment;
    }

    public void displayInfo() {
        System.out.println("Name: " + javaDevelopment.getName());
        System.out.println("Job: " + javaDevelopment.getJob());
    }
}
Es gibt auch Main – eine Klasse, die alle Abteilungen verwaltet. (Auflistung 3)
package org.example;

public class Main {
    public static void main(String ... args){
        JavaDevelopment javaDevelopment = new JavaDevelopment();
        HiringDepartment hiringDepartment = new HiringDepartment(javaDevelopment);

        hiringDepartment.displayInfo();
    }
}
Stabilität vorerst. Wenn wir die Main-Klasse ausführen, erhalten wir das folgende Ergebnis:
Name: Alexa
Job: Middle Java developer
Stellen wir uns nun vor, dass es dem Unternehmen gut geht. Daher beschlossen sie, den Umfang ihrer Aktivitäten zu erweitern und eröffneten eine Python-Entwicklungsabteilung. Und hier stellt sich die Frage: Wie beschreibt man diese Abteilung auf Programmebene? Antwort: Sie müssen „kopieren und einfügen“, wo immer Sie diese Abteilung beschreiben möchten (die gute alte Methode🙃). Erstellen wir zunächst die Klasse selbst, die die Abteilung „Pythonisten“ beschreiben würde. (Auflistung 4)
package org.example;

public class PythonDevelopment {
    public String getName(){
        return "Mike";
    }

    public String getJob(){
        return "Middle Python developer";
    }
}
Und dann werden wir es an die Einstellungsabteilung weiterleiten. Und HiringDepartment sagt nichts über diese Abteilung. Daher müssen Sie ein neues Objekt der PythonDevelopment-Klasse und einen Konstruktor erstellen, der Python-Entwickler akzeptiert. Sie müssen außerdem die Methode displayInfo() ändern, damit die Informationen korrekt angezeigt werden. (Auflistung 5)
package org.example;

public class HiringDepartment {
    private JavaDevelopment javaDevelopment;

    public HiringDepartment(JavaDevelopment javaDevelopment) {
        this.javaDevelopment = javaDevelopment;
    }


    //Тут создается отдел найма для Python - разработчиков
    private PythonDevelopment pythonDevelopment;

    public HiringDepartment(PythonDevelopment pythonDevelopment) {
        this.pythonDevelopment = pythonDevelopment;
    }

    //Тогда придется изменить метод displayInfo()
    public void displayInfo() {
        if(javaDevelopment != null) {
            System.out.println("Name: " + javaDevelopment.getName());
            System.out.println("Job: " + javaDevelopment.getJob());
        } else if (pythonDevelopment != null){
            System.out.println("Name: " + pythonDevelopment.getName());
            System.out.println("Job: " + pythonDevelopment.getJob());
        }
    }
}
Wie wir sehen können, hat sich das Codevolumen verdoppelt oder sogar noch mehr. Bei großer Codemenge nimmt die Lesbarkeit ab. Und das Schlimmste ist, dass wir alle Objekte manuell erstellen und Klassen erstellen, die stark voneinander abhängig sind. Ok, wir waren damit einverstanden. Sie haben nur eine Abteilung beschrieben. Wir werden dadurch nichts verlieren. Was wäre, wenn wir eine weitere Abteilung hinzufügen würden? Was ist, wenn es zwei sind? Drei? Aber niemand verbot „Bergbau und Weidewirtschaft“. Frühling.  Lektion 2. IoC/DI in der Praxis – 1 Ja, niemand hat „Mine and Pasture“ verboten, aber es ist nicht professionell. Tyzh ist Programmierer. Und hier können Sie DI verwenden. Das heißt, wir werden nicht auf Klassenebene, sondern auf Schnittstellenebene arbeiten. Jetzt werden die Zustände unserer Objekte in Schnittstellen gespeichert. Auf diese Weise werden Abhängigkeiten zwischen Klassen minimal. Dazu erstellen wir zunächst die Entwicklungsoberfläche, die über zwei Methoden zur Beschreibung eines Mitarbeiters verfügt. (Auflistung 6)
package org.example;

public interface Development {
    String getName();
    String getJob();
}
Lassen Sie dann zwei Klassen JavaDevelopment und PythonDevelopment von dieser Schnittstelle implementieren (erben) und die Methoden String getName() und String getJob() überschreiben. (Auflistung 7, 8)
package org.example;

public class JavaDevelopment implements Development {
    @Override
    public String getName(){
        return "Alexa";
    }

    @Override
    public String getJob(){
        return "Middle Java developer";
    }
}
package org.example;

public class PythonDevelopment implements Development {
    @Override
    public String getName(){
        return "Mike";
    }

    @Override
    public String getJob(){
        return "Middle Python developer";
    }
}
Dann können Sie in der HiringDepartment-Klasse einfach ein Schnittstellenobjekt vom Typ Development definieren und ein solches Objekt auch an den Konstruktor übergeben. (Auflistung 9)
package org.example;

public class HiringDepartment {
    private Development development; //Определяем интерфейс

    //Конструктор принимает ein Objekt интерфейса
    public HiringDepartment(Development development){
        this.development = development;
    }

    public void displayInfo(){
        System.out.println("Name: " + development.getName());
        System.out.println("Job: " + development.getJob());
    }
}
Wie wir sehen können, ist die Codemenge zurückgegangen. Und das Wichtigste: Abhängigkeiten wurden minimiert. Wie werden Werte und Abhängigkeiten eigentlich für diese Objekte umgesetzt? Es gibt drei Möglichkeiten, eine Abhängigkeitsinjektion durchzuführen:
  • Verwendung des Konstruktors
  • Setter verwenden
  • Autowiring (automatische Bindung)
Implementierung mit einem Konstruktor Lassen Sie uns nun über die Implementierung mit einem Konstruktor sprechen. Schauen Sie sich Listing 9 an. Der Konstruktor der HiringDepartment-Klasse erwartet ein Objekt vom Typ Development als Eingabe. Wir werden versuchen, über diesen Konstruktor Abhängigkeiten einzufügen. Erwähnenswert ist auch, dass die Abhängigkeitsinjektion mithilfe sogenannter Spring-Container erfolgt. Es gibt drei Möglichkeiten, Spring-Container zu konfigurieren:
  • Verwendung von XML-Dateien (veraltete Methode)
  • Verwendung von Anmerkungen + XML-Dateien (moderne Art)
  • Verwendung von Java-Code (moderne Art)
Wir verwenden jetzt die Konfiguration mithilfe von XML-Dateien. Obwohl diese Methode als veraltet gilt, werden viele Projekte immer noch auf diese Weise geschrieben. Deshalb müssen Sie es wissen. Zuerst müssen Sie eine XML-Datei im Ressourcenordner erstellen. Sie können ihm einen beliebigen Namen geben, vorzugsweise jedoch einen aussagekräftigen. Ich habe es „applicationContext.xml“ genannt. Frühling.  Lektion 2. IoC / DI in der Praxis - 2 In diese Datei schreiben wir den folgenden Code (Listing 10):
<?xml version="1.0" encoding="UTF-8"?>
<beans  xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="javaDeveloper" class="org.example.JavaDevelopment"/>
    <bean id="pythonDeveloper" class="org.example.PythonDevelopment"/>

    <bean id="hiringDepartment" class="org.example.HiringDepartment">
        <constructor-arg ref="javaDeveloper"/>
    </bean>

</beans>
Nun der Reihe nach. Die ersten acht Codezeilen sind für uns nicht interessant, sie sind Standard. Sie können sie einfach kopieren. Das Tag <bean> </bean> definiert eine Spring-Bean. Eine Bean ist ein Objekt, das von einem Spring-Container erstellt und verwaltet wird. Mit einfachen Worten: Der Spring-Container selbst erstellt ein neues Klassenobjekt für uns (zum Beispiel: JavaDevelopment javaDevelopment = new JavaDevelopment();). Innerhalb dieses Tags befinden sich ID- und Klassenattribute. id gibt den Namen der Bean an. Diese ID wird für den Zugriff auf das Objekt verwendet. Es entspricht dem Namen eines Objekts in einer Java-Klasse. Klasse – definiert den Namen der Klasse, an die unsere Bean (Objekt) gebunden ist. Sie müssen den vollständigen Pfad zur Klasse angeben. Achten Sie auf die Bean „hiringDepartment“. In dieser Bean gibt es ein weiteres <constructor-arg ref="javaDeveloper"/>-Tag. Hier erfolgt die Abhängigkeitsinjektion (in unserem Fall die Injektion mithilfe eines Konstruktors). <constructor-arg> – teilt Spring mit, dass der Spring-Container nach Abhängigkeiten im im Bean-Attribut definierten Klassenkonstruktor suchen soll. Und welches Objekt zugeordnet werden muss, wird durch das ref- Attribut im <constructor-arg>-Tag bestimmt. ref – gibt die ID der Bean an, mit der Kontakt aufgenommen werden soll. Wenn wir in ref anstelle von javaDeveloper die ID pythonDeveloper angeben, erfolgt die Verbindung mit der Klasse PythonDevelopmen. Jetzt müssen wir die Main-Klasse beschreiben. Es wird so aussehen: (Listing11)
package org.example;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String ... args){
        //Определяем контекст файл в котором содержатся прописанные нами бины
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        //Получем бины, которые были определены в файле applicationContext.xml
        HiringDepartment hiringDepartment = context.getBean("hiringDepartment", HiringDepartment.class);

        hiringDepartment.displayInfo();

        context.close(); //Контекст всегда должен закрываться
    }
}
Was gibt es hier?
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Diese Zeile verknüpft die Main-Klasse mit der XML-Datei, die unsere Beans beschreibt. Der an den Konstruktor übergebene Wert muss mit dem Namen der XML-Datei übereinstimmen. (In unserem Fall applicationContext.xml).
HiringDepartment hiringDepartment = context.getBean("hiringDepartment", HiringDepartment.class);
Gibt an, dass wir eine Bean (Objekt) der HiringDepartment-Klasse erhalten möchten. Das erste Argument verweist auf die Bean-ID, die wir in die XML-Datei geschrieben haben. Das zweite Argument zeigt auf die Klasse, mit der wir Kontakt aufnehmen möchten. Dieser Vorgang wird als Reflexion bezeichnet .
hiringDepartment.displayInfo();
 context.close(); //Контекст всегда должен закрываться
Hier erhalten wir ganz einfach eine Methode der HiringDepartment-Klasse. Beachten Sie, dass wir zum Abrufen der Objekte nicht das Schlüsselwort new verwendet haben und nirgendwo abhängige Objekte vom Typ JavaDevelopment oder PythonDevelopment definiert haben. Sie wurden einfach in der Datei applicationContext.xml beschrieben. Achten Sie auch auf die letzte Zeile. Sie sollten den Kontext immer schließen, bevor Sie ihn herunterfahren. Andernfalls werden keine Ressourcen freigegeben und es kann zu einem Speicherverlust oder einer fehlerhaften Ausführung des Programms kommen. Wenn Sie Fragen oder Anregungen haben, schreiben Sie in die Kommentare, ich werde auf jeden Fall antworten. Danke für die Aufmerksamkeit. Quellcode unter dem Link Mein GitHub- Warenkorb Kursinhalt Fortsetzung folgt...
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION