JavaRush /Java 博客 /Random-ZH /春天。第 2 课:IoC/DI 实践
Umaralikhon
第 3 级
Красноярск

春天。第 2 课:IoC/DI 实践

已在 Random-ZH 群组中发布
所以...上一课我们简单回顾了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