41、工厂模式(下)

1、工厂模式和 DI 容器有何区别

DI 容器底层最基本的设计思路就是基于工厂模式的
DI 容器相当于一个大的工厂类,负责在程序启动的时候,根据配置(要创建哪些类对象,每个类对象的创建需要依赖哪些其他类对象)事先创建好对象
当应用程序需要使用某个类对象的时候,直接从容器中获取即可,正是因为它持有一堆对象,所以这个框架才被称为 "容器"

DI 容器相对于我们上节课讲的工厂模式的例子来说,它处理的是更大的对象创建工程
上节课讲的工厂模式中,一个工厂类只负责某个类对象或者某一组相关类对象(继承自同一抽象类或者接口的子类)的创建
而 DI 容器负责的是整个应用中所有类对象的创建,除此之外,DI 容器负责的事情要比单纯的工厂模式要多,它还包括配置的解析、对象生命周期的管理

2、DI 容器的核心功能有哪些

  • 配置解析
  • 对象创建
  • 对象生命周期管理

2.1、配置解析

在上节课讲的工厂模式中,工厂类要创建哪个类对象是事先确定好的,并且是写死在工厂类代码中的
作为一个通用的框架来说,框架代码跟应用代码应该是高度解耦的,DI 容器事先并不知道应用会创建哪些对象,不可能把某个应用要创建的对象写死在框架代码中
所以我们需要通过一种形式,让应用告知 DI 容器要创建哪些对象,这种形式就是我们要讲的配置

我们将需要由 DI 容器来创建的类对象和创建类对象的必要信息(使用哪个构造函数以及对应的构造函数参数都是什么等等),放到配置文件中
容器读取配置文件,根据配置文件提供的信息来创建对象

下面是一个典型的 Spring 容器的配置文件
Spring 容器读取这个配置文件,解析出要创建的两个对象:rateLimiter 和 redisCounter,并且得到两者的依赖关系:rateLimiter 依赖 redisCounter

public class RateLimiter {
    private RedisCounter redisCounter;

    public RateLimiter(RedisCounter redisCounter) {
        this.redisCounter = redisCounter;
    }

    public void test() {
        System.out.println("Hello World!");
    }
    //...
}

public class RedisCounter {
    private String ipAddress;
    private int port;

    public RedisCounter(String ipAddress, int port) {
        this.ipAddress = ipAddress;
        this.port = port;
    }
    //...
}
<beans>
    <bean id="rateLimiter" class="com.xzg.RateLimiter">
        <constructor-arg ref="redisCounter"/>
    </bean>

    <bean id="redisCounter" class="com.xzg.redisCounter">
        <constructor-arg type="String" value="127.0.0.1"/>
        <constructor-arg type="int" value="1234"/>
    </bean>
</beans>

2.2、对象创建

在 DI 容器中,如果我们给每个类都对应创建一个工厂类,那项目中类的个数会成倍增加,这会增加代码的维护成本
要解决这个问题并不难,我们只需要将所有类对象的创建都放到一个工厂类中完成就可以了,比如 BeansFactory

你可能会说,如果要创建的类对象非常多,BeansFactory 中的代码会不会线性膨胀(代码量跟创建对象的个数成正比)呢,实际上并不会
待会讲到 DI 容器的具体实现的时候,我们会讲 "反射" 这种机制,它能在程序运行的过程中,动态地加载类、创建对象,不需要事先在代码中写死要创建哪些对象
所以不管是创建一个对象还是十个对象,BeansFactory 工厂类代码都是一样的

2.3、对象生命周期管理

上一节课我们讲到,简单工厂模式有两种实现方式,一种是每次都返回新创建的对象,另一种是每次都返回同一个事先创建好的对象,也就是所谓的单例对象
在 Spring 框架中,我们可以通过配置 scope 属性,来区分这两种不同类型的对象
scope=prototype 表示返回新创建的对象,scope=singleton 表示返回单例对象

除此之外,我们还可以配置对象是否支持懒加载
如果 lazy-init=true,对象在真正被使用到的时候(BeansFactory.getBean("userService"))才被被创建
如果 lazy-init=false,对象在应用启动的时候就事先创建好

不仅如此,我们还可以配置对象的 init-method 和 destroy-method 方法
比如 init-method=loadProperties(),destroy-method=updateConfigFile()
DI 容器在创建好对象之后,会主动调用 init-method 属性指定的方法来初始化对象
在对象被最终销毁之前,DI 容器会主动调用 destroy-method 属性指定的方法来做一些清理工作,比如释放数据库连接池、关闭文件

3、如何实现一个简单的 DI 容器

posted @ 2023-06-26 15:20  lidongdongdong~  阅读(4)  评论(0编辑  收藏  举报