JavaRush /Java Blog /Random-TW /春天。第 2 課:IoC/DI 實踐
Umaralikhon
等級 3
Красноярск

春天。第 2 課:IoC/DI 實踐

在 Random-TW 群組發布
所以...上一課我們簡單回顧了IoC和DI的理論部分。我們也為我們的專案設定了 pom.xml 設定檔。今天我們開始創建程式的主要部分。首先,我將向您展示如何在沒有 IoC/DI 的情況下建立程式。然後我們就直接創建一個獨立引入依賴的程式。也就是說,程式碼的控制權傳遞到框架手中(聽起來令人毛骨悚然)。 當我們管理程序時, 想像有一家公司。公司(目前)有兩個部門:Java 開發部和招募部。讓描述「Java開發部門」的類別有兩個方法:String getName() - 傳回員工的姓名,String getJob() - 傳回員工的職位。(清單 1)
package org.example;

public class JavaDevelopment {

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

    public String getJob(){
        return "Middle Java developer";
    }
}
讓描述招募部門的類別有一個接受員工的輸入建構函數,以及一個顯示有關員工資訊的 void displayInfo() 方法。(清單 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());
    }
}
還有Main——管理所有部門的類別。(清單 3)
package org.example;

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

        hiringDepartment.displayInfo();
    }
}
目前穩定。當我們運行 Main 類別時,我們得到以下結果:
Name: Alexa
Job: Middle Java developer
現在讓我們想像一下這家公司做得很好。因此,他們決定擴大活動範圍,並開設了Python開發部門。那麼問題來了:如何在程序層面描述這個部門?答:只要你需要描述這個部門,你就需要「複製並貼上」(很好的老方法🙃)。首先,讓我們創建類別本身,它將描述“Pythonists”部門。(清單 4)
package org.example;

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

    public String getJob(){
        return "Middle Python developer";
    }
}
然後我們將其轉移到招聘部門。而 HiringDepartment 對此部門隻字未提。因此,您必須建立 PythonDevelopment 類別的新物件和接受 Python 開發人員的建構子。您還必須更改 displayInfo() 方法,以便它可以正確顯示資訊。(清單 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());
        }
    }
}
正如我們所看到的,程式碼量增加了一倍,甚至更多。程式碼量大了,可讀性就會下降。最糟糕的是我們手動創建所有物件並創建彼此高度依賴的類別。好的,我們同意這一點。他們只是描述了一個部門。我們不會因此而失去任何東西。那麼,如果我們再增加一個部門呢?如果有兩個怎麼辦?三?但沒有人禁止「採礦和放牧」。 春天。 第 2 課:IoC / DI 實作 - 1 是的,沒有人禁止“礦山和牧場”,但這並不專業。Tyzh 是一名程式設計師。這裡你可以使用DI。也就是說,我們不會在類別層級工作,而是在介面層級工作。現在我們物件的狀態將儲存在介面中。這樣,類別之間的依賴關係將最小化。為此,我們首先建立 Development 接口,該接口有兩種用於描述員工的方法。(清單 6)
package org.example;

public interface Development {
    String getName();
    String getJob();
}
然後讓 JavaDevelopment 和 PythonDevelopment 兩個類別實作(繼承)該介面並重寫 String getName() 和 String getJob() 方法。(清單 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";
    }
}
然後在 HiringDepartment 類別中,您可以簡單地定義一個 Development 類型的介面對象,也可以將這樣的物件傳遞給建構子。(清單 9)
package org.example;

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

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

    public void displayInfo(){
        System.out.println("Name: " + development.getName());
        System.out.println("Job: " + development.getJob());
    }
}
正如我們所看到的,程式碼量減少了。最重要的是,依賴性被最小化。這些物件的值和依賴關係實際上是如何實現的?依賴注入有以下三種方式:
  • 使用建構函數
  • 使用設定器
  • 自動裝配(自動綁定)
使用建構函數的實作 現在我們來談談使用建構函式的實作。請參閱清單 9。HiringDepartment 類別的建構子需要一個 Development 類型的物件作為輸入。我們將嘗試透過此建構函數注入依賴項。另外值得注意的是,依賴注入是使用所謂的 Spring 容器執行的。配置 Spring 容器有以下三種方式:
  • 使用 XML 檔案(過時的方法)
  • 使用註解+XML檔案(現代方式)
  • 使用Java程式碼(現代方式)
我們現在使用 XML 檔案進行配置。儘管這種方法被認為已經過時了,但許多專案仍然採用這種方式編寫。因此你需要知道。首先,您必須在資源資料夾中建立一個 xml 檔案。您可以給它任何名稱,但最好是一個有意義的名稱。我將其命名為“applicationContext.xml”。 春天。 第 2 課:IoC / DI 實作 - 2 在此文件中,我們將編寫以下程式碼(清單 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>
現在,按順序。前八行程式碼我們不感興趣,它們是預設的。您可以簡單地複製它們。<bean> </bean> 標籤定義了一個Spring bean。 bean是由 Spring 容器建立和管理的物件。簡單來說,Spring容器本身為我們建立了一個新的類別物件(例如:JavaDevelopment javaDevelopment = new JavaDevelopment();)。該標籤內有 id 和 class 屬性。id指定 bean 的名稱。該 id 將用於存取該物件。它相當於Java類別中物件的名稱。class - 定義我們的 bean(物件)綁定到的類別的名稱。您必須指定類別的完整路徑。注意僱傭部門 bean。在這個 bean 內部還有另一個 <constructor-arg ref="javaDeveloper"/> 標記。這就是依賴注入發生的地方(在我們的例子中,使用構造函數注入)。<constructor-arg> - 告訴 Spring Spring 容器應該在 bean 屬性中定義的類別建構子中尋找依賴項。需要與哪個物件關聯由<constructor-arg> 標記內的ref屬性決定。ref - 指示要聯絡的 bean 的 id。如果在 ref 而不是 javaDeveloper 中我們指定 id pythonDeveloper,則與 PythonDevelopmen 類別發生連接。現在我們需要描述 Main 類別。它看起來像這樣:(清單 11)
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(); //Контекст всегда должен закрываться
    }
}
這裡是什麼?
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
此行將 Main 類別連結到描述我們的 bean 的 .xml 檔案。傳遞給建構函數的值必須與 .xml 檔案的名稱相符。(在我們的例子中是applicationContext.xml)。
HiringDepartment hiringDepartment = context.getBean("hiringDepartment", HiringDepartment.class);
表示我們想要接收 HiringDepartment 類別的 bean(物件)。第一個參數指向我們在 xml 檔案中寫入的 bean id。第二個參數指向我們想要聯繫的類別。這個過程稱為反射
hiringDepartment.displayInfo();
 context.close(); //Контекст всегда должен закрываться
這裡我們很容易得到HiringDepartment類別的一個方法。請注意,我們沒有使用 new 關鍵字來取得對象,也沒有在任何地方定義 JavaDevelopment 或 PythonDevelopment 類型的依賴對象。它們在 applicationContext.xml 檔案中進行了簡單的描述。還要注意最後一行。您應該始終在關閉之前關閉上下文。否則,資源將無法被釋放,並且可能會出現記憶體洩漏或程式錯誤運行。如果有疑問或建議,請寫在評論裡,我一定會解答。感謝您的關注。原始碼位於連結 我的GitHub 購物車 課程內容 待續...
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION