JavaRush /Blog Java /Random-VI /Mùa xuân. Bài 2. IoC/DI trong thực tế
Umaralikhon
Mức độ
Красноярск

Mùa xuân. Bài 2. IoC/DI trong thực tế

Xuất bản trong nhóm
Và vì vậy... Trong bài học trước chúng ta đã ôn sơ qua phần lý thuyết của IoC và DI. Chúng tôi cũng thiết lập tệp cấu hình pom.xml cho dự án của mình. Hôm nay chúng ta bắt đầu tạo phần chính của chương trình. Đầu tiên tôi sẽ hướng dẫn bạn cách tạo một chương trình không có IoC/DI. Và sau đó chúng ta sẽ trực tiếp tạo một chương trình giới thiệu các phần phụ thuộc một cách độc lập. Tức là quyền kiểm soát mã sẽ được chuyển vào tay của khung (nghe có vẻ rùng rợn). Trong khi chúng ta đang quản lý chương trình, hãy tưởng tượng rằng có một công ty nào đó. Và công ty (hiện tại) có hai bộ phận: Phòng tuyển dụng và phát triển Java. Cho lớp mô tả “Bộ phận phát triển Java” có hai phương thức: String getName() – trả về tên nhân viên, String getJob() – trả về vị trí của nhân viên. (Liệt kê 1)
package org.example;

public class JavaDevelopment {

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

    public String getJob(){
        return "Middle Java developer";
    }
}
Hãy để lớp mô tả bộ phận tuyển dụng có một hàm tạo đầu vào chấp nhận một nhân viên và phương thức void displayInfo() hiển thị thông tin về nhân viên. (Liệt kê 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());
    }
}
Ngoài ra còn có Main - lớp quản lý tất cả các phòng ban. (Liệt kê 3)
package org.example;

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

        hiringDepartment.displayInfo();
    }
}
Sự ổn định cho đến nay. Khi chạy lớp Main chúng ta nhận được kết quả như sau:
Name: Alexa
Job: Middle Java developer
Bây giờ hãy tưởng tượng rằng công ty đang hoạt động rất tốt. Vì vậy, họ quyết định mở rộng phạm vi hoạt động và mở bộ phận phát triển Python. Và ở đây câu hỏi được đặt ra: Làm thế nào để mô tả bộ phận này ở cấp độ chương trình? Trả lời: bạn cần “sao chép và dán” bất cứ nơi nào bạn cần để mô tả bộ phận này (phương pháp cũ hay🙃). Đầu tiên, hãy tự tạo lớp, lớp này sẽ mô tả bộ phận "Pythonists". (Liệt kê 4)
package org.example;

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

    public String getJob(){
        return "Middle Python developer";
    }
}
Và sau đó chúng tôi sẽ chuyển nó đến Bộ phận tuyển dụng. Và Bộ phận tuyển dụng không nói gì về bộ phận này. Do đó, bạn sẽ phải tạo một đối tượng mới của lớp PythonDevelopment và một hàm tạo chấp nhận các nhà phát triển Python. Bạn cũng sẽ phải thay đổi phương thức displayInfo() để nó hiển thị thông tin chính xác. (Liệt kê 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());
        }
    }
}
Như chúng ta có thể thấy, khối lượng mã đã tăng gấp đôi hoặc thậm chí nhiều hơn. Với số lượng mã lớn, khả năng đọc của nó sẽ giảm. Và điều tệ nhất là chúng ta tạo tất cả các đối tượng theo cách thủ công và tạo ra các lớp phụ thuộc nhiều vào nhau. Được rồi, chúng tôi đã đồng ý với điều này. Họ chỉ mô tả một bộ phận. Chúng ta sẽ không mất gì từ việc này. Chà, nếu chúng ta thêm một bộ phận khác thì sao? Nếu có hai thì sao? Ba? Nhưng không ai cấm việc “khai thác và chăn thả”. Mùa xuân.  Bài 2. IoC/DI trong thực tế - 1 Đúng, không ai cấm “Mỏ và đồng cỏ”, nhưng nó không chuyên nghiệp. Tyzh là một lập trình viên. Và ở đây bạn có thể sử dụng DI. Nghĩa là, chúng ta sẽ làm việc không phải ở cấp độ lớp mà ở cấp độ giao diện. Bây giờ trạng thái của các đối tượng của chúng ta sẽ được lưu trữ trong các giao diện. Bằng cách này, sự phụ thuộc giữa các lớp sẽ ở mức tối thiểu. Để làm điều này, trước tiên chúng ta tạo giao diện Phát triển, có hai phương thức để mô tả một nhân viên. (Liệt kê 6)
package org.example;

public interface Development {
    String getName();
    String getJob();
}
Sau đó, hãy để hai lớp JavaDevelopment và PythonDevelopment triển khai (kế thừa) từ giao diện này và ghi đè các phương thức String getName() và String getJob(). (Liệt kê 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";
    }
}
Sau đó, trong lớp HiringDepartment, bạn có thể chỉ cần định nghĩa một đối tượng giao diện thuộc loại Development và bạn cũng có thể chuyển một đối tượng như vậy cho hàm tạo. (Liệt kê 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());
    }
}
Như chúng ta có thể thấy, số lượng mã đã giảm. Và quan trọng nhất, sự phụ thuộc đã được giảm thiểu. Các giá trị và phần phụ thuộc thực sự được triển khai như thế nào cho các đối tượng này? Có ba cách để thực hiện chèn phần phụ thuộc:
  • Sử dụng hàm tạo
  • Sử dụng setters
  • Autowiring (ràng buộc tự động)
Triển khai bằng cách sử dụng hàm tạo Bây giờ hãy nói về việc triển khai bằng cách sử dụng hàm tạo. Hãy xem Liệt kê 9. Hàm tạo của lớp HiringDepartment mong đợi một đối tượng thuộc kiểu Development làm đầu vào. Chúng tôi sẽ cố gắng chèn các phần phụ thuộc thông qua hàm tạo này. Cũng cần lưu ý rằng việc tiêm phụ thuộc được thực hiện bằng cách sử dụng cái gọi là bộ chứa Spring. Có ba cách để định cấu hình bộ chứa Spring:
  • Sử dụng tệp XML (Phương pháp lỗi thời)
  • Sử dụng chú thích + tệp XML (Cách hiện đại)
  • Sử dụng mã Java (Cách hiện đại)
Chúng tôi hiện đang sử dụng cấu hình bằng các tệp XML. Mặc dù thực tế là phương pháp này được coi là lỗi thời nhưng nhiều dự án vẫn được viết theo cách này. Vì vậy bạn cần phải biết. Trước tiên, bạn phải tạo một tệp xml trong thư mục tài nguyên. Bạn có thể đặt cho nó bất kỳ tên nào, nhưng tốt nhất là tên có ý nghĩa. Tôi gọi nó là "applicationContext.xml". Mùa xuân.  Bài 2. IoC/DI trong thực tế - 2 Trong tệp này, chúng ta sẽ viết đoạn mã sau (Liệt kê 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>
Bây giờ, theo thứ tự. Tám dòng mã đầu tiên không gây hứng thú cho chúng tôi, chúng là mặc định. Bạn chỉ có thể sao chép chúng. Thẻ <bean> </bean> định nghĩa một Spring Bean. Bean là một đối tượng được tạo và quản lý bởi Spring container. Nói một cách đơn giản, Spring container tự tạo ra một đối tượng lớp mới cho chúng ta (ví dụ: JavaDevelopment javaDevelopment = new JavaDevelopment();). Bên trong thẻ này có các thuộc tính id và class. id chỉ định tên của đậu. Id này sẽ được sử dụng để truy cập vào đối tượng. Nó tương đương với tên của một đối tượng trong lớp Java. class - xác định tên của lớp mà Bean (đối tượng) của chúng ta bị ràng buộc. Bạn phải chỉ định đường dẫn đầy đủ đến lớp. Hãy chú ý đến bộ phận tuyển dụng. Bên trong hạt đậu này có một thẻ <constructor-arg ref="javaDeveloper"/> khác. Đây là nơi xảy ra việc tiêm phụ thuộc (trong trường hợp của chúng tôi là việc tiêm bằng cách sử dụng hàm tạo). <constructor-arg> - báo cho Spring rằng bộ chứa Spring nên tìm kiếm các phần phụ thuộc trong hàm tạo của lớp được xác định trong thuộc tính Bean. Và đối tượng nào cần liên kết được xác định bởi thuộc tính ref , bên trong thẻ <constructor-arg>. ref - cho biết id của đậu cần liên hệ. Nếu trong ref thay vì javaDeveloper, chúng ta chỉ định id pythonDeveloper, thì kết nối sẽ xảy ra với lớp PythonDevelopmen. Bây giờ chúng ta cần mô tả lớp Main. Nó sẽ trông như thế này: (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(); //Контекст всегда должен закрываться
    }
}
Cái gì đây?
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Dòng này liên kết lớp Main với tệp .xml mô tả các Bean của chúng ta. Giá trị được truyền cho hàm tạo phải khớp với tên của tệp .xml. (Trong trường hợp của chúng tôi là applicationContext.xml).
HiringDepartment hiringDepartment = context.getBean("hiringDepartment", HiringDepartment.class);
Cho biết rằng chúng ta muốn lấy một Bean (đối tượng) của lớp HiringDepartment. Đối số đầu tiên trỏ tới id đậu mà chúng ta đã viết trong tệp xml. Đối số thứ hai trỏ đến lớp mà chúng ta muốn liên hệ. Quá trình này được gọi là sự phản ánh .
hiringDepartment.displayInfo();
 context.close(); //Контекст всегда должен закрываться
Ở đây chúng ta dễ dàng có được một phương thức của lớp HiringDepartment. Lưu ý rằng chúng tôi không sử dụng từ khóa mới để lấy các đối tượng và chúng tôi không xác định các đối tượng phụ thuộc thuộc loại JavaDevelopment hoặc PythonDevelopment ở bất kỳ đâu. Chúng được mô tả đơn giản trong tệp applicationContext.xml. Cũng chú ý đến dòng cuối cùng. Bạn phải luôn đóng ngữ cảnh trước khi tắt. Nếu không, tài nguyên sẽ không được giải phóng và có thể xảy ra rò rỉ bộ nhớ hoặc hoạt động không chính xác của chương trình. Nếu bạn có thắc mắc hoặc gợi ý, hãy viết bình luận, tôi chắc chắn sẽ trả lời. Cảm ơn bạn đã chú ý. Mã nguồn tại liên kết Giỏ hàng GitHub của tôi Nội dung khóa học Còn tiếp...
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION