JavaRush /Java Blog /Random-KO /봄. 2과. IoC/DI의 실제 사례
Umaralikhon
레벨 3
Красноярск

봄. 2과. IoC/DI의 실제 사례

Random-KO 그룹에 게시되었습니다
그래서... 이전 강의에서 우리는 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());
    }
}
보시다시피 코드의 양이 줄었습니다. 그리고 가장 중요한 것은 종속성이 최소화되었다는 것입니다. 이러한 객체에 대한 값과 종속성은 실제로 어떻게 구현됩니까? 종속성 주입을 수행하는 방법에는 세 가지가 있습니다.
  • 생성자 사용
  • 세터 사용
  • Autowiring(자동 바인딩)
생성자를 사용한 구현 이제 생성자를 사용한 구현에 대해 이야기해 보겠습니다. 목록 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>
이제 순서대로. 코드의 처음 8줄은 우리에게 흥미롭지 않으며 기본값입니다. 간단히 복사할 수 있습니다. <bean> </bean> 태그는 Spring Bean을 정의합니다. Bean은 Spring 컨테이너에 의해 생성되고 관리되는 객체입니다. 간단히 말해서 Spring 컨테이너 자체는 새로운 클래스 객체를 생성합니다(예: JavaDevelopment javaDevelopment = new JavaDevelopment();). 이 태그 안에는 id 및 class 속성이 있습니다. id는 빈의 이름을 지정합니다. 이 ID는 개체에 액세스하는 데 사용됩니다. 이는 Java 클래스의 객체 이름과 동일합니다. 클래스 - 빈(객체)이 바인딩되는 클래스의 이름을 정의합니다. 클래스의 전체 경로를 지정해야 합니다. HireingDepartment 빈에 주목하세요. 이 Bean 내부에는 또 다른 <constructor-arg ref="javaDeveloper"/> 태그가 있습니다. 여기에서 종속성 주입이 발생합니다(이 경우 생성자를 사용한 주입). <constructor-arg> - Spring 컨테이너가 Bean 속성에 정의된 클래스 생성자에서 종속성을 찾아야 함을 Spring에게 알려줍니다. 그리고 어떤 객체와 연결되어야 하는지는 <constructor-arg> 태그 내부의 ref 속성에 의해 결정됩니다. ref - 접속할 Bean의 ID를 나타냅니다. javaDeveloper 대신 ref에서 pythonDeveloper ID를 지정하면 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