工厂, 模板方法模式优化Bean容器

日志

在上一节中,只是简单创建了 BeanDefinition 和 BeanFactory

本章节有以下变动:

  • 使用单例模式注册 Bean 容器,从而实现对象第二次获取时可以从内存中获取对象
  • 使用工厂模式将 Bean 的创建过程交给容器,而不是在调用时期传递一个实例化好的 Bean 对象
  • 使用模板模式统一通用核心方法的调用逻辑和标准定义

设计

完善 Spring Bean:注册 Register 和获取 Get

架构图

image-20230206190106725

注册 Register

提示

非常重要的一点是 Bean 注册时只注册一个类信息,而不会直接将实例化信息注册到 Spring 容器中,所以我们修改 BeanDefinition 中的属性 bean,将类型从 Object 转换为 Class。

负责 bean 对象第一次注册,之后存储到 单例对象缓存中,Get 直接从内存中获取

获取 Get

提示

获取 Bean 对象时就需要处理 Bean 的对象实例化操作以及判断当前单例对象是否在容器里缓存起来。

  1. 定义 BeanFactory 也就是 Bean 工厂接口,提供 Bean 获取方法 getBean(String name)
  2. 这个接口由抽象类 AbstractBeanFacotry 实现,其中使用模板模式实现通用核心方法的统一调度,让后续的实现不需要关心调用逻辑,只需关心具体的实现
  3. 继承抽象类 AbstractBeanFactory 后的 AbstractAutowireCapableBeanFactory 实现 Bean 的实例化
  4. 定义 SingletonBeanRegistry 单例接口,用 DefaultSingletonBeanRegistry 对接口实现后,会被抽象类 AbstractBeanFactory 继承。

工程

Spring Bean 容器类关系

image-20230221164540596

  • BeanFactory 接口工厂通过 AbstractBeanFactory 抽象类实现接口的 getBean 方法
  • AbstractBeanFactory 又继承了实现了 SingletonBeanRegistry 的 DefaultSingletonBeanRegister 类,从而让其具有单例 Bean 的注册功能
  • AbstractBeanFactory 定义两个抽象方法:
    • getBeanDefinition(String beanName),其由 DefaultListableBeanFacotry 实现
    • createBean(String beanName, BeanDefinition beanDefinition),其由 AbstractAutowireCapableBeanFactory 实现
  • DefaultListableBeanFactory 继承抽象类 AbstractAutowireCapableBeanFactory 也就可以调用抽象类中的 createBean 方法了

BeanDefinition 定义

public class BeanDefinition {

private Class beanClass;

public BeanDefinition(Class beanClass) {
this.beanClass = beanClass;
}
// ...get/set

}
  • 在 Bean 定义类中已经把上一章节中的 Object bean 替换为 Class,这样就可以把 Bean 的实例化操作放到容器中处理了。本质上是通过类的反射实例化 Bean 对象

单例注册接口定义和实现

Spring中bean的默认作用域就是singleton。

以下内容看不明白可暂时跳过

除了 singleton(单例作用域),Spring bean还有下面几种作用域:

  • prototype(原型作用域) : 每次请求都会创建一个新的 bean 实例。
  • request: 每一次HTTP请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。
  • session: 每一次HTTP请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。

Spring实现单例的方式:

xml格式:<bean id="userService" class="top.snailclimb.UserService" scope="singleton"/>
注解:@Scope(value = "singleton")

定义 BeanFactory 工厂

这里使用工厂模式,目的是延迟注入,也就是使用到某个 bean 时才会注入这个 bean 对象的相关信息。配合上单例模式缓存已经注册的 bean 信息,相较于 ApplicationContext 占用的内存更少,启动速度更快

而 ApplicationContext ,在容器启动的时候,不管你用没用到,一次性创建所有 bean 。BeanFactory 仅提供了最基本的依赖注入支持,ApplicationContext 扩展了 BeanFactory ,除了有 BeanFactory 的功能还有额外更多功能,所以一般开发人员使用ApplicationContext 会更多。

public interface BeanFactory {

/**
* 获取 Bean 实例对象
*/
Object getBean(String name) throws BeansException;

}

抽象类定义模板方法(AbstractBeanFactory)

public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {

@Override
public Object getBean(String name) throws BeansException {
Object bean = getSingleton(name);
if (bean != null) {
// 单例 bean 对象已经存在于缓存中,直接获取返回
return bean;
}

// 获取不到 bean,说明是第一次注册,通过 bean 的定义完成实例化操作
BeanDefinition beanDefinition = getBeanDefinition(name);
return createBean(name, beanDefinition);
}

protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;

protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException;
}
  • AbstractBeanFactory 首先继承了 DefaultSingletonBeanRegistry,也就具备了使用单例注册类方法。
  • 接下来很重要的一点是关于接口 BeanFactory 的实现,在方法 getBean 的实现过程中可以看到,主要是对单例 Bean 对象的获取以及在获取不到时需要拿到 Bean 的定义做相应 Bean 实例化操作。那么 getBean 并没有自身的去实现这些方法,而是只定义了调用过程以及提供了抽象方法,由实现此抽象类的其他类做相应实现。
  • 后续继承抽象类 AbstractBeanFactory 的类有两个,包括:AbstractAutowireCapableBeanFactory、DefaultListableBeanFactory,这两个类分别做了相应的实现处理

实例化Bean类(AbstractAutowireCapableBeanFactory)

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException {
Object bean;
try {
// 从 BeanDefinition 获取 Bean 的类信息,实例化完整的类,但只能实例无参构造对象
bean = beanDefinition.getBeanClass().newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new BeansException("实例化容器失败 Instantiation of bean failed", e);
}
// 将注册好的实例 Bean 存入单例对象缓存中
addSingleton(beanName, bean);
return bean;
}

}
  • 在 AbstractAutowireCapableBeanFactory 类中实现了 Bean 的实例化操作 newInstance,其实这块会埋下一个坑,有构造函数入参的对象怎么处理?可以提前思考
  • 在处理完 Bean 对象的实例化后,直接调用 addSingleton 方法存放到单例对象的缓存中去。

核心类实现(DefaultListableBeanFactory)

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry {

private final Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

@Override
protected BeanDefinition getBeanDefinition(String beanName) throws BeansException {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition == null) {
throw new BeansException("No bean named '" + beanName + "' is defined" + " -- '" + beanName + "' 没有被定义");
}
return beanDefinition;
}

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
beanDefinitionMap.put(beanName, beanDefinition);
}
}
  • DefaultListableBeanFactory 在 Spring 源码中也是一个非常核心的类,它对 Spring IoC 提供了生产和获取 Bean 的方法,DefaultListableBeanFactory 继承了 AbstractAutowireCapableBeanFactory 类,也就具备了接口 BeanFactory 和 AbstractBeanFactory 等一连串的功能实现。所以有时候你会看到一些类的强转,调用某些方法,也是因为你强转的类实现接口或继承了某些类。
  • 除此之外这个类还实现了接口 BeanDefinitionRegistry 中的 registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法,当然你还会看到一个 getBeanDefinition 的实现,这个方法我们文中提到过它是抽象类 AbstractBeanFactory 中定义的抽象方法。现在注册Bean定义与获取Bean定义就可以同时使用了,是不感觉这个套路还蛮深的。接口定义了注册,抽象类定义了获取,都集中在 DefaultListableBeanFactory 中的 beanDefinitionMap 里

测试

@Test
public void test_BeanFactory(){
// 1.初始化 BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 2.注册 bean
BeanDefinition beanDefinition = new BeanDefinition(UserService.class);
beanFactory.registerBeanDefinition("userService", beanDefinition);
// 3.第一次获取 bean
UserService userService = (UserService) beanFactory.getBean("userService");
userService.queryUserInfo();
// 4.第二次获取 bean from Singleton
UserService userService_singleton = (UserService) beanFactory.getBean("userService");
userService_singleton.queryUserInfo();
}
  • 在此次的单元测试中除了包括;Bean 工厂、注册 Bean、获取 Bean,三个步骤,还额外增加了一次对象的获取和调用。这里主要测试验证单例对象的是否正确的存放到了缓存中。
  • 此外与上一章节测试过程中不同的是,我们把 UserService.class 传递给了 BeanDefinition 而不是像上一章节那样直接 new UserService() 操作。

测试结果

查询用户信息
查询用户信息

Process finished with exit code 0
  • 这里会有两次测试信息,一次是获取 Bean 时直接创建的对象,另外一次是从缓存中获取的实例化对象。
  • 此外从调试的截图中也可以看到第二次获取单例对象,已经可以从内存中获取了,如图 3-3图片
  • 到这本章节的功能实现和测试验证就完成了,关于测试过程中可以再去断点调试下各个阶段类的调用,熟悉调用关系。