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 容器
本文来自博客园,作者:lidongdongdong~,转载请注明原文链接:https://www.cnblogs.com/lidong422339/p/17505738.html