【重写SpringFramework】第一章beans模块:初步认识BeanFactory(chapter 1-4)
1. 前言
之前我们提到,控制反转的含义是指 IOC 容器接管了对象的创建权。举个例子,对象 A 依赖 B,但实际上 A 并不关心 B 是怎么来的,只要 IOC 容器能提供给它就行。因此在代码层面,BeanFactory
应用了简单工厂模式,屏蔽了产品的生产过程,提供给消费者的是一个可用的产品。
BeanFactory
的基本结构包括三个部分,如下图所示。首先是工厂方法,客户端调用 getBean
方法就可以获得指定的对象。其次是 Bean 的缓存,IOC 容器将自身所管理的所有 Bean 缓存起来,在整个应用程序中实现资源共享。第三,addSingleton
方法的作用是向缓存中添加一个对象。我们发现这种结构形成了一个闭环系统,有输入,有输出,还有存储。本节的目标便是实现一个最简单的 Spring 容器。
2. BeanFactory 体系介绍
2.1 继承结构
BeanFactory
的继承体系比较庞大,各个类和接口之间的关系也很复杂。为了让读者有一个直观的认识,下图列出了 Spring 源码中 BeanFactory
的继承结构。
显而易见,过于复杂的结构对于理解源码造成了很大的困难。因此,我们对原始的继承体系进行了精简,并根据功能的不同划分为三个部分。第一组是红色部分,负责单例的管理工作。
SingletonBeanRegistry
:负责注册单例的接口DefaultSingletonBeanRegistry
:注册单例的实现类,用于存储 Spring 管理的单例FactoryBeanRegistrySupport
:用于存储FactoryBean
类型的单例所包装的对象
第二组是蓝色部分,负责 Bean 的管理工作,实现了 BeanFactory
的核心功能。
BeanFactory
:Spring 容器的核心接口,定义了管理 Bean 的方法AutowireCapableBeanFactory
:拥有自动装配的能力,也就是通常所说的依赖注入ConfigurableBeanFactory
:负责对BeanFactory
进行相关配置,外界可以访问该接口AbstractBeanFactory
:定义了获取 Bean 的主要流程,将实际的创建工作交由子类完成AbstractAutowireCapableBeanFactory
:完成了 Bean 的实际创建工作,包括实例化、自动装配、初始化等功能DefaultListableBeanFactory
:默认的BeanFactory
接口的实现类
第三组是黄色部分,负责 BeanDefinition 的管理工作,只有一个接口,孤悬于整个体系之外。
BeanDefinitionRegistry
:实现了注册BeanDefinition
的功能
2.2 角色分类
以上介绍了 BeanFactory
各个接口和类的基本功能,分别对 Bean、单例和 BeanDefinition
进行管理。那么这三者之间有什么联系和区别,如下所示:
- Bean:泛指各种对象,可以由
BeanFactory
创建,也可以由外部创建。需要注意的,Spring 容器并不对所有对象进行管理。 - 单例:一种特殊的对象,由 Spring 容器进行管理。所谓管理是指除了创建对象,还负责缓存该对象。缓存则意味着持有该项资源,这是 IOC 容器的基础。
BeanDefinition
:定义了创建对象的细节信息,是 Spring 容器创建对象的依据。(下节介绍)
注:一个 Bean 是否能成为单例是由其作用域决定的,singleton 表示这是一个单例,而 prototype 表示该对象是原型对象,也就是说每次都创建一个新的对象,Spring 容器也不对其进行管理。我们不关心原型作用域的实现,此处仅作为单例的扩展内容加以讨论。
3. 单例管理
3.1 SingletonBeanRegistry
单例是一种特殊的 Bean,表示在应用程序中唯一存在的对象。单例由 Spring 容器进行管理,包括创建、存储、销毁等,因此也称为容器托管(managed)对象。SingletonBeanRegistry
接口定义了管理单例的相关方法,Bean 的名称唯一决定一个单例。
registerSingleton
方法:注册一个单例,需要指定名称。参数singletonObject
表示一个外部创建的对象,由于对象的创建不是由 Spring 容器掌管的,因此这种注册方式很少使用。getSingleton
方法:获取指定名称的单例containsSingleton
方法:判断指定名称的单例是否存在
public interface SingletonBeanRegistry {
void registerSingleton(String beanName, Object singletonObject);
Object getSingleton(String beanName);
boolean containsSingleton(String beanName);
}
3.2 DefaultSingletonBeanRegistry
DefaultSingletonBeanRegistry
是 SingletonBeanRegistry
接口的默认实现类,完成了单例的注册、查找、销毁等功能。singletonObjects
是最重要的属性,作用是存储已创建的单例。它是一个 ConcurrentHashMap
类型的对象,支持在多线程的情况下安全地进行操作。
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
//缓存单例
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
//缓存beanName
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
}
registerSingleton
方法的逻辑比较简单,首先查找缓存中是否存在实例,如果没有则将对象添加到单例缓存中。需要注意的是,方法入参 singletonObject
表示已存在的对象,是外界 new 出来的,而不是 Spring 容器创建的。因此,registerSingleton
方法只是注册单例的一种可选途径,并不是最主要的方式(仅了解)。
//注册单例
@Override
public void registerSingleton(String beanName, Object singletonObject){
synchronized (this.singletonObjects) {
Object obj = this.singletonObjects.get(beanName);
if (obj != null) {
throw new IllegalStateException("注册单例Bean失败,[" + beanName + "]已存在");
}
addSingleton(beanName, singletonObject);
}
}
//将单例添加到缓存中
protected void addSingleton(String beanName, Object singletonObject) {
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
this.registeredSingletons.add(beanName);
}
getSingleton
方法的作用是从缓存中查找单例,这是接口定义的方法,实际的查找工作委托给同名的重载方法。重载的 getSingleton
方法只进行了简单的处理,直接从缓存中获取实例。之后实现依赖注入的功能时,会出现循环依赖的问题,届时再会回过头来重构该方法。
//查找单例
@Override
public Object getSingleton(String beanName){
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//TODO 简单实现,解决循环依赖的问题时再完善
return this.singletonObjects.get(beanName);
}
需要注意的是,该类有三个重载的 getSingleton
方法,本节我们用到了前两个方法,简单介绍如下:
getSingleton(String beanName)
:从缓存中查找单例,这是接口定义的方法,供外界使用getSingleton(String beanName, boolean allowEarlyReference)
:查找单例的实际逻辑,是受保护的方法,由子类调用。getSingleton(String beanName, ObjectFactory<?> singletonFactory)
:从缓存中查找单例,如不存在则创建新对象。换句话说,Spring 容器创建对象实际上是通过该方法完成的。
3.3 FactoryBeanRegistrySupport
FactoryBeanRegistrySupport
的作用是处理 FactoryBean
类型的特殊单例,暂时不做介绍,仅作为继承体系中的一环存在。本教程的讲解原则是先介绍通用的情况,再考虑特殊的情况,否则会顾此失彼,抓不住重点。
4. Bean 的管理
4.1 BeanFactory
BeanFactory
是整个 Spring 容器的核心接口,为了简化继承结构,还将 ListableBeanFactory
接口合并进来。BeanFactory
接口定义了若干管理 Bean 的方法,根据功能将这些方法划分为三组。第一组是 getBean
系列方法,不仅可以获取实例,还包含了创建流程。第二组与 Bean 的类型有关。第三组是 ListableBeanFactory
接口的方法,特点是以枚举的方式遍历所有的 Bean,而不是像 getBean
方法一次只能获取一个实例。
public interface BeanFactory {
Object getBean(String name);
<T> T getBean(String name, Class<T> requiredType);
<T> T getBean(Class<T> requiredType) throws BeansException;
Class<?> getType(String name);
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws BeansException;
boolean isTypeMatch(String name, Class<?> typeToMatch) throws BeansException;
boolean containsBeanDefinition(String beanName);
List<String> getBeanNamesForType(Class<?> type);
<T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException;
}
4.2 ConfigurableBeanFactory
ConfigurableBeanFactory
接口的作用是对 Spring 容器进行一些配置工作,比如设置一些组件,或者完成某些功能。目前只列出了本节涉及到的方法,其余方法将在后续介绍。同时为了简化继承结构,将 ConfigurableListableBeanFactory
接口合并进来了。
public interface ConfigurableBeanFactory extends AutowireCapableBeanFactory, SingletonBeanRegistry {
void setParentBeanFactory(BeanFactory parentBeanFactory) throws IllegalStateException;
TypeConverter getTypeConverter();
}
4.3 AbstractBeanFactory
AbstractBeanFactory
是一个抽象类,实现了 ConfigurableBeanFactory
接口。同时还继承了 FactoryBeanRegistrySupport
类,拥有了管理单例的能力。AbstractBeanFactory
最重要的工作就是实现了一系列 getBean
方法,它们最终都调用了 doGetBean
方法。doGetBean
方法是获取 Bean 的核心方法,后续会详细分析流程,目前仅调用父类的 getSington
方法查询缓存即可。
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
//父容器
private BeanFactory parentBeanFactory;
//转换服务
private ConversionService conversionService = new DefaultConversionService();
//类型转换器
private TypeConverter typeConverter = new SimpleTypeConverter();
@Override
public Object getBean(String name) throws RuntimeException {
return doGetBean(name, null);
}
@Override
public <T> T getBean(String name, Class<T> requiredType) {
return doGetBean(name, requiredType);
}
//获取Bean的核心方法
protected <T> T doGetBean(final String name, final Class<T> requiredType) {
return (T)getSingleton(name);
}
}
注:本节暂不介绍
AbstractAutowireCapableBeanFactory
和DefaultListableBeanFactory
,仅定义空的实现,用于创建BeanFactory
实例。(代码略)
5. 测试
测试方法比较简单,首先创建 DefaultListableBeanFactory
实例,调用 registerSingleton
方法添加一个单例,且 User
对象是手动创建的。然后调用 getBean
方法获取指定名称的单例。
@Test
public void testGetBean() {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.registerSingleton("foo", new User("Foo", 18));
Object bean = factory.getBean("foo");
System.out.println("注册手动创建的对象: " + bean);
}
从测试结果可以看到,注册到 Spring 容器中的对象可以取出来。如果多调用几次 getBean
方法,得到的仍是同一个对象,而这正是单例的特点。
注册手动创建的对象: User{name='Foo', age=18}
6. 总结
本节初步认识了 Spring 实现的 IOC 容器,我们对 BeanFactory
的继承体系进行划分,其中 SingletonBeanRegistry
接口负责管理单例,BeanFactory
接口则是对 Bean 进行管理。两者的区别在于,Bean 泛指各种对象,而单例是一种特殊的 Bean,由 Spring 容器进行管理。
Spring 容器的基本工作包括两个方面,一是注册单例,二是获取单例。Spring 提供了两种注册单例的方式,一是通过 SingletonBeanRegistry
接口注册一个对象,这是直接的方式;二是通过 BeanDefinitionRegistry
接口注册 BeanDefinition
,间接地实现对象的创建和注册。本节仅实现了第一种方式,虽然实现简单,但并不常用,因为对象的创建过程并不是由容器掌握的。
7. 项目信息
本节新增和修改内容一览,新增项(13),修改项(0)。
beans
└─ src
├─ main
│ └─ java
│ └─ cn.stimd.spring.beans
│ └─ factory
│ ├─ config
│ │ ├─ AutowireCapableBeanFactory.java (+)
│ │ ├─ ConfigurableBeanFactory.java (+)
│ │ └─ SingletonBeanRegistry.java (+)
│ ├─ support
│ │ ├─ AbstractAutowireCapableBeanFactory.java (+)
│ │ ├─ AbstractBeanFactory.java (+)
│ │ ├─ DefaultListableBeanFactory.java (+)
│ │ ├─ DefaultSingletonBeanRegistry.java (+)
│ │ └─ FactoryBeanRegistrySupport.java (+)
│ └─ BeanFactory.java (+)
└─ test
└─ java
└─ beans
└─ factory
├─ User.java (+)
└─ FactoryTest.java (+)
注:+号表示新增、*表示修改
注:项目的 master 分支会跟随教程的进度不断更新,如果想查看某一节的代码,请选择对应小节的分支代码。
欢迎关注公众号【Java编程探微】,回复「重写SpringFramework」加群一起讨论。
原创不易,觉得内容不错请分享一下。
转载自:https://juejin.cn/post/7372396174248624166