利用模板模式对IOC功能分层

Meteor 2023-10-09 10:52:37
Categories: Tags:

简介

在上一篇中实现了极简IOC,将IOC的流程大概讲述了一遍,从本篇开始对这个IOC进行功能增强

上一篇中直接将实例化好的对象放入 BeanDefiniton 中,本篇改为将 Class 对象放入 BeanDefinition 中,然后在 BeanFactory 中获取 Bean 时,先从 singletonObjects(存储Bean对象的Map,保证了单例) 中尝试获取该 Bean ,若能获取到,直接返回,否则将根据 BeanDefinition 中存储的 Class 对象进行实例化,然后将实例化后的对象放入单例对象的Map中,最后将对象返回

建议先把本篇所涉及的各个类的功能以及继承关系大致搞清楚,再将代码手敲一遍,最后再将正文过一遍

源码:small-spring02

继承关系图

下图是实际上使用的 BeanFactory 的 继承关系图,可在IDEA中选中 DefaultListableBeanFactory,右键点击 Diagrams -> Show Diagrams 查看下图,按照顺序从上往下依次查看各个类的功能

接下来开始详细讲解如何通过模板模式分层的,参照上面的继承关系图观看效果更好,当然更加建议手动打开 Diagrams ,然后显示字段和方法,可以将字段和方法的继承看的更加清楚

模板分层

模板模式:有一个顶层抽象类或接口定义了方法的规范,由其子类逐渐实现该方法的细节

分层的主线是 BeanFacotry -> AbstractBeanFactory -> AbstractAutowireCapableBeanFactory -> DefaultListableBeanFactory ,代表了 BeanFactory 的功能实现一步步进行分层

副线一是 SingletonBeanRegistry -> DefaultSingletonBeanRegistry -> AbstractBeanFactory ,目的是给 AbstractBeanFactory 赋予注册、获取单例对象的功能

副线二是 BeanDefinitionRegistry -> DefaultListableBeanFactory ,目的是给 DefaultListableBeanFactory 赋予注册 BeanDefinition 的能力

最好按照 副线一 -> 主线 -> 副线二 的顺序阅读,其中 副线二 在本篇中因为功能不强其实可有可无,但实际上必不可少

主线

BeanFacotry

IOC的顶层接口是 BeanFactory ,定义了 getBean() 的方法,BeansException 是自定义异常,代码贴在最后

package com.meteor.factory;

import com.meteor.BeansException;

public interface BeanFactory {
Object getBean(String name) throws BeansException;
}

AbstractBeanFactory

BeanFactory 的抽象子类,也是模板分层的核心类,实现了 getBean() 方法,但只定义了具体实现的步骤,先从 singletonObjects 中尝试获取该 Bean ,若能获取到则直接返回,否则从 beanDefinitionMap 中获取 BeanDefinition,然后根据 BeanDefinition 中存储的信息创建 Bean,可以看到,具体的实现细节都封装成了 protected 类型的抽象方法交由子类实现了

AbstractBeanFactory 继承了 DefaultSingletonBeanRegistry ,singletonObjects 在 DefaultSingletonBeanRegistry 中定义,因此有了从 singletonObjects 中获取单例对象的能力,关于 DefaultSingletonBeanRegistry ,下面在副线一里会介绍

package com.meteor.factory.support;

import com.meteor.BeansException;
import com.meteor.factory.BeanFactory;
import com.meteor.factory.config.BeanDefinition;

public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {
@Override
public Object getBean(String name) throws BeansException {
Object bean = getSingleton(name);
if (bean != null) {
return bean;
}
BeanDefinition beanDefinition = getBeanDefinition(name);
return createBean(name, beanDefinition);
}

//获取 BeanDefinition ,由子类 DefaultListableBeanFactory 实现
protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;

//创建 Bean ,由子类 AbstractAutowireCapableBeanFactory 实现
protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException;
}

AbstractAutowireCapableBeanFactory

AbstractBeanFactory 的子类,实现了 createbean() 方法,根据 beanDefinition 中存储的 BeanClass 对象进行实例化,之后将创建成功的对象添加到 singletonObjects 中,最后返回创建好的对象

注意:此处直接根据 BeanClass 对象利用反射调用无参构造方法创建实例,因此如果没有无参构造方法或者将无参构造设置为 private 就会报 InstantiationException 异常。这个问题将在下一篇解决

package com.meteor.factory.support;

import com.meteor.BeansException;
import com.meteor.factory.config.BeanDefinition;

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException {
Object bean = null;
try {
bean = beanDefinition.getBeanClass().newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new BeansException("Instantiation of bean failed", e);
}
addSingleton(beanName, bean);
return bean;
}
}

DefaultListableBeanFactory

作为模板模式的最终实现类,也是我们实际使用的 BeanFacotry ,内部定义了一个 beanDefinitionMap 用于存储 BeanDefinition ,实现了 getBeanDefinition() 的功能,同时实现了 BeanDefinitionRegistry 接口的注册 BeanDefinition 方法

package com.meteor.factory.support;

import com.meteor.BeansException;
import com.meteor.factory.config.BeanDefinition;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry {
private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

@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");
}
return beanDefinition;
}

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
beanDefinitionMap.put(beanName, beanDefinition);
}
}

副线一

SingletonBeanRegistry

单例注册接口,提供了获取单例对象的功能,在IOC中的 Bean 对象默认是单例的,因此 BeanFaactory 需要先判断对象是否已经创建,如果已经创建好,那就不应该再创建,创建好的对象存储在 SingletonBeanRegistry 的子类 DefaultSingletonBeanRegistry 定义的 singletonObjects 中

package com.meteor.factory.config;

public interface SingletonBeanRegistry {
Object getSingleton(String beanName);
}

DefaultSingletonBeanRegistry

SingletonBeanRegistry 的默认实现类,提供了获取、注册单例对象的功能,定义了 singletonObjects 用以存储单例对象

package com.meteor.factory.support;

import com.meteor.factory.config.SingletonBeanRegistry;

import java.util.HashMap;
import java.util.Map;

public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
private Map<String, Object> singletonObjects = new HashMap<>();

@Override
public Object getSingleton(String beanName) {
return singletonObjects.get(beanName);
}

protected void addSingleton(String beanName, Object singletonObject) {
singletonObjects.put(beanName, singletonObject);
}
}

副线二

BeanDefinitionRegistry

定义了注册 BeanDefinition 的规范,目的是给 DefaultListableBeanFactory 进行功能增强

package com.meteor.factory.support;

import com.meteor.factory.config.BeanDefinition;

public interface BeanDefinitionRegistry {
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
}

测试

同样的,测试之前先提供一个服务类作为我们想要从IOC容器中获取到的 Bean 对象,此时由于默认提供了一个空参构造方法,因此可以通过测试,但如果添加一个有参构造方法,那么测试将会失败,失败原因在 AbstractAutowireCapableBeanFactory 已经说明

UserService

package com.meteor.bean;

public class UserService {
public void queryUserInfo() {
System.out.println("查询用户信息");
}
}

开始测试

依然是四个步骤,初始化 BeanFacotry 、将 Bean 注册到 BeanDefinition 中 、 将 BeanDefinition 注册到 BeanFacotry 中 、 从 BeanFactory 中获取 Bean ,只是具体的实现方式发生了变化

建议从执行 beanFactory.getBean() 开始 debug,了解 BeanFacotry 如何在获取 Bean 的时候保证单例以及根据 BeanDefiniton 中的信息创建实例的

package com.meteor;

import com.meteor.bean.UserService;
import com.meteor.factory.config.BeanDefinition;
import com.meteor.factory.support.DefaultListableBeanFactory;
import org.junit.Test;

public class MainTest {
@Test
public void testBeanFactory() throws BeansException {
// 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
UserService userService1 = (UserService) beanFactory.getBean("userService");
userService1.queryUserInfo();

// 验证两个bean是否是同一个
System.out.println(userService == userService1);
}
}

输出结果:

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

Process finished with exit code 0

总结

本篇的目的就是再次确定IOC的几个核心概念,BeanDefiniton 存储创建 Bean 对象的信息,目前只有 Class 信息,因此只能根据无参构造实例化 Bean,之后需要将 Bean 的字段信息也写进去,这样就能调用有参构造了

BeanFactory 获取 Bean,当然,在 Bean 未实例化时,要根据 BeanDefinition 中存储的信息创建 Bean ,并保证 Bean 是单例的。本篇将 BeanFactory 的功能实现进行分层后,实际使用的是最后一个子类 DefaultListableBeanFactory ,它汇聚了所有的功能

目前为止,代码中有两个Map,一个是 beanDefinitionMap ,由 DefaultListableBeanFactory 定义,用于存储 BeanDefinition ,一个是 singletonObjects ,由 DefaultSingletonBeanRegistry 定义,用于存储实例化后的 Bean 对象,保证单例

本篇的问题在于 AbstractAutowireCapableBeanFactory 创建 Bean 时,直接通过反射调用 Class.newInstance() 实例化对象,当 Bean 没有无参构造时就会出错,下一篇将解决这个问题

目录结构

相较于上一篇更新的类标注update,新增的类标注为new

src
├── main
│ └── java
│ └── com.meteor
│ ├── factory
│ │ ├── config
│ │ │ ├── BeanDefinition.java (update)
│ │ │ └── SingletonBeanRegistry.java (new)
│ │ ├── support
│ │ │ ├── AbstractAutowireCapableBeanFactory.java (new)
│ │ │ ├── AbstractBeanFactory.java (new)
│ │ │ ├── BeanDefinitionRegistry.java (new)
│ │ │ ├── DefaultListableBeanFactory.java (new)
│ │ │ └── DefaultSingletonBeanRegistry.java (new)
│ │ └── BeanFactory.java (update)
│ └── BeansException.java (new)
└── test
└── java
└── com.meteor
├── bean
│ └── UserService.java
└── MainTest.java

依赖

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>