【重写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

DefaultSingletonBeanRegistrySingletonBeanRegistry 接口的默认实现类,完成了单例的注册、查找、销毁等功能。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) {
        returnTgetSingleton(name);
    }
}

注:本节暂不介绍 AbstractAutowireCapableBeanFactoryDefaultListableBeanFactory,仅定义空的实现,用于创建 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 (+)

注:+号表示新增、*表示修改
  • 项目地址:https://gitee.com/stimd/spring-wheel

  • 本节分支:https://gitee.com/stimd/spring-wheel/tree/chapter1-4

注:项目的 master 分支会跟随教程的进度不断更新,如果想查看某一节的代码,请选择对应小节的分支代码。


关注公众号【Java编程探微】,加群一起讨论。
在这里插入图片描述

  • 15
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值