Spring IOC源码地图

5,502 阅读8分钟

阅读源码很重要的一点就是找到切入点,即从哪里开始看源码。一个很简单的思路就是从自己日常开发中常用到的 api 开始看,原因也很简单,一个是满足自己的好奇心,毕竟整天用却还不知道底层原理,还有一个原因是防止以后出现问题可以不那么慌,毕竟了解了原理还是多一份底气。

版本约定

spring-framework-5.3.13

Bean 的变化过程

具体流程

因为 Bean 的中文释义是“豆子”,而且 Spring 是一门 Java 的技术,Java 的 Logo 是一杯咖啡,所以我们可以把 Bean 的变化过程比作是咖啡豆的加工处理过程,如 图 1.1.1.2 所示。

我们可以把 Spring 容器当成一个生产咖啡豆的公司,这个公司有五个各司其职的部门,如 图 1.1.1.1 所示。

  • “总部”:负责整个公司日常的运营以及对各个子部门的调度。
  • “采购部”:负责将卖家的资源采购到公司。
  • “农业技术部”:负责将资源进行识别和统计。
  • “农业生产部”:负责将采摘和初步加工原始的咖啡豆。
  • “作物加工部门”:负责生产成品咖啡豆即之前的所有加工程序。

图 1.1.1.1

咖啡豆具体的加工过程如 图 1.1.1.2 所示。

  1. ”总部“将打听到的咖啡豆种植园的地址记录到名为 Location 的 excel 表格并发给“采购部门”
  2. ”采购部门“把 Location 上的种植园都买下来并且把植物园的详细信息记录到名为 Resource 的 excel 表格并发给”农业技术部“
  3. ”农业技术部“调查每棵 Resource 里的咖啡树并将咖啡树的信息记录到名为 Document 的 excel 表格
  4. ”农业技术部“根据 Document 把每棵咖啡树上的咖啡豆都采摘下来并给每颗咖啡豆贴上产品信息标签
  5. ”作物加工部“通过水系处理后日晒脱水,最后给没有咖啡豆包上包装

图 1.1.1.2

对应实际的组件

图 1.1.1.1 对应的实际组件如下 ↓图 1.1.2.1,根据位置一一对应的。

图 1.1.2.1

  • ApplicationContext:这是用于向应用程序提供配置信息的中央接口,它提供了应用程序所需要的所有配置,如 BeanFactoryResourceLoaderApplicationEventPublisher 等等。
  • 配置元信息:即 xml 、properties 等配置文件,ApplicationContext 会读取配置元信息的 URI
  • 配置信息 URI 数组:这是多个配置元信息在文件系统的路径的数组,ResourcePatternResolver 会读取配置信息 URI 数组里每一个 URI 对应的文件并封装成 Resource 对象
  • ResourcePatternResolver:用于将位置模式(例如,Ant 风格的路径模式)解析为 Resource 对象的策略接口
  • Resource:从底层资源的实际类型(例如文件或类路径资源)中抽象出来的资源描述符的接口
  • BeanDefinitionReader:用于读取 bean definition
  • Document:代表整个 HTML 或 XML 文档。从概念上讲,它是文档树的根,并提供对文档数据的主要访问
  • BeanDefinitionParserDelegate:用于解析 XML bean 定义的有状态委托类
  • BeanDefinition:描述了一个 bean 实例,它具有属性值、构造函数参数值以及由具体实现提供的更多信息
  • BeanDefinitionHolder:带有名称和别名的 BeanDefinition 的持有者
  • BeanFactory:访问 Spring bean 容器的根接口
  • RootBeanDefinition:一个在运行时的 Spring BeanFactory 中可合并多个 bean definition 的 BeanDefinition
  • BeanWrapper:是 Spring 的低级 JavaBeans 基础结构的中心接口,提供分析和操作标准 JavaBeans 的操作,即获取和设置属性值(单独或批量)、获取属性描述符和查询属性的可读性和可写性的能力

图 1.1.1.2

对应实际的流程

图 1.1.1.2 对应的实际组件如下 ↓图 1.1.3.1,根据位置一一对应的。

  1. 生成配置信息 URI 数组: AbstractRefreshableConfigApplicationContext 使用 getConfigLocations() 获取配置文件所在路径并把路径数组存到 String[] configLocations 中。
  2. 生成 Resource: ResourcePatternResolver 使用 getResources() 从 第一步中加载文件信息封装成 Resource,并把 Resource 存到 XmlBeanDefinitionReaderThreadLocal 中。
  3. 生成 Document: XmlBeanDefinitionReader 使用 loadDocument() 从第二步中加载 Resource 并封装成 Document
  4. 生成 BeanDefinitionHolder
    1. 处理 Bean 标签 BeanDefinitionParserDelegate 使用 parseBeanDefinitionElement()Document 转化成 BeanDefinition 并将 BeanDefinition 存储到 DefaultListableBeanFactory 中的 Map<String, BeanDefinition> beanDefinitionMap,最后会将 BeanDefinition 封装成 BeanDefinitionHolder
    2. 处理自定义命名空间节点: ClassPathScanningCandidateComponentProvider 使用 findCandidateComponents()Document 转化成 BeanDefinition 并将 BeanDefinition 存储到 DefaultListableBeanFactory 中的 Map<String, BeanDefinition> beanDefinitionMap。 最后 ClassPathBeanDefinitionScanner 使用 doScan() 将 BeanDefinition 封装成 BeanDefinitionHolder
  5. 生成 RootBeanDefinitionAbstractBeanFactory 使用 getMergedBeanDefinition()BeanDefinitionHolder 封装成 RootBeanDefinition,并将 RootBeanDefinition 存储到 Map<String, RootBeanDefinition> mergedBeanDefinitions
  6. 生成 Bean 实例 Object: AbstractAutowireCapableBeanFactory 使用 instantiateBean() 通过 RootBeanDefinition 实例化 Bean 实例,并将生成的实例存储到 DefaultSingletonBeanRegistry 中的三级缓存 (后面会详细讲解到)。

图 1.1.3.1

  • AbstractRefreshableApplicationContext
    • ApplicationContext 的实现类,它应该支持对 refresh() (refresh 在后面会有讲解) 的多次调用,每次都创建一个新的内部 bean 工厂实例。通常,这样的上下文将读取一组配置文件的 URI,以从中加载 bean definition

    • UML

      image-20211231142246810

  • XmlBeanDefinitionReader
    • 用于读取 xml bean definition。

    • UML

      image-20211211223512679

  • ClassPathBeanDefinitionScanner
    • 一个 bean definition 扫描器,用于检测类路径上的 bean 候选者,使用给定的注册表 BeanFactoryApplicationContext 注册相应的 bean definition。
  • ClassPathScanningCandidateComponentProvider
    • 从基础包中提供候选组件的组件提供者。如果可用,可以使用 CandidateComponentsIndex 否则扫描类路径。通过应用排除和包含过滤器来识别候选组件。
  • DefaultSingletonBeanRegistry
    • 共享 bean 实例的通用注册表,实现 SingletonBeanRegistry。 允许注册应该为注册表的所有调用者共享的单例实例,通过 bean 名称获取。
    • Snipaste_2021-12-11_22-15-32
      • AbstractBeanFactory
        • BeanFactory 的实现类,提供了 ConfigurableBeanFactory SPI 的全部功能。 可以用作 bean 工厂实现的基类,这些实现从某些后端资源获取 bean definition。
        • AbstractAutowireCapableBeanFactory
          • 实现默认 bean 创建的抽象 bean 工厂超类,具有 RootBeanDefinition 类指定的全部功能,提供 bean 创建(具有构造函数解析)、属性填充、装配(包括自动装配)和初始化。
          • DefaultListableBeanFactory
            • Spring 对 ConfigurableListableBeanFactory 和 BeanDefinitionRegistry 接口的默认实现:基于 bean definition 元数据的成熟 bean 工厂,可通过后处理器扩展。

图 1.1.3.1 过渡到 下图

image-20211229170607217.png

再稍微添加亿点点细节,得到下图: image-20211229171432248.png

⭐我把以上流程制作成了很形象的 ppt 动画,强烈建议跳转到 语雀 查看

Bean 的生命周期

《Pro Spring 5》中这样描述 Bean 的生命周期: 图 2.1.1

《Spring in Action》中这样描述: 图 2.1.2

对比两图可以看出两者描述的内容基本一致,只有红线框住的地方不一样,但是都是 Aware 接口的,在官方文档中可以找到 Spring 常用的 Aware 接口:

图 2.1.3

证明 图 2.1.1 图 2.1.2 的接口都是存在的。有了以上信息,我们可以将一个 Bean 实现以上提到的关于 Bean 生命周期的接口,去验证实现了这些接口是否有效果,若有效果是否是和书中提到的执行顺序一致?

POJO ↓代码 2.1.1

public class Apple implements ResourceLoaderAware, ApplicationContextAware, BeanFactoryAware, BeanNameAware,
        MessageSourceAware, ApplicationEventPublisherAware, EnvironmentAware, InitializingBean, DisposableBean, BeanClassLoaderAware {
            
    public void initMethod() {
        System.out.println("init-method");
    }

    public void destroyMethod() {
        System.out.println("destroy-method");
    }

    @PostConstruct
    public void before() {
        System.out.println("BeanPostProcessor-postProcessBeforeInitialization");
    }

    @PreDestroy
    public void after() {
        System.out.println("BeanPostProcessor-postProcessAfterInitialization");
    }

    @Override
    public void setMessageSource(MessageSource messageSource) {
        System.out.println("MessageSourceAware-setMessageSource");
    }

    @Override
    public void setBeanName(String name) {
        taste = name;
        System.out.println("BeanNameAware-setBeanName");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("BeanFactoryAware-setBeanFactory");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("ApplicationContextAware-setBeanFactory");
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        System.out.println("ApplicationEventPublisherAware-setApplicationEventPublisher");
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        System.out.println("ResourceLoaderAware-setResourceLoader");
    }

    @Override
    public void setEnvironment(Environment environment) {
        System.out.println("EnvironmentAware-setEnvironment");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean-afterPropertiesSet");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean-destroy");
    }
            
    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("BeanClassLoaderAware-setBeanClassLoader");
    }
}

配置类 ↓代码 2.1.2

@Configuration
@ComponentScan(value = {"com.vacodwave.*"})
public class MyConfiguration {
    @Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
    public Apple apple(){
        return new Apple();
    }
}

main 类测试 ↓代码 2.1.3

public class Main {
    public static void main(String[] args) throws Exception {
        AbstractApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
        Apple apple = context.getBean(Apple.class);
        // 此方法用于关闭容器,触发 destroy-method 执行
        context.close();
    }
}

输出结果 ↓结果 2.1.4 证明 图 2.1.2图 2.1.3 中 BeanNameAware 及之后的步骤是正确的

BeanNameAware-setBeanName
BeanClassLoaderAware-setBeanClassLoader
BeanFactoryAware-setBeanFactory
EnvironmentAware-setEnvironment
ResourceLoaderAware-setResourceLoader
ApplicationEventPublisherAware-setApplicationEventPublisher
MessageSourceAware-setMessageSource
ApplicationContextAware-setApplicationContext
BeanPostProcessor-postProcessBeforeInitialization
InitializingBean-afterPropertiesSet
init-method
BeanPostProcessor-postProcessAfterInitialization
DisposableBean-destroy
destroy-method

BeanNameAware 之前的步骤的正确性可以在源码地图中得到验证,最后我再自己绘制一张关于 Bean 生命周期的图:

image-20211230175847506

refresh() 方法

在上面了解清楚了 Bean 生命周期,那么这些接口的执行是在源码的哪个地方如何执行的呢?

通过 debug 可以发现 BeanNameAware 到 @PreDestroy 的步骤都是在 AbstractAutowireCapableBeanFactory 的 initializeBean() 中执行,而 initializeBean() 是在 refesh() 方法里的 finishBeanFactoryInitialization 阶段,对应源码地图中的“初始化 bean” 模块

图 2.1.2.1

refresh() 方法主要负责加载、注册和实例化 bean ,涉及到大部分 Spring IOC 的原理和功能,refresh 内执行的流程如:

图 2.1.2.2 根据 refresh() 方法的结构以及 debug 每个阶段的过程,Bean 的生命周期图可以在 图 2.1.2.2 的基础上添加与 refresh() 方法 、源码的联系:

图 2.1.2.3

参考

[1]Spring Team.Spring Source Code[DB]

[2] 郝佳.Spring 源码深度解析 [M].China: 异步社区, 2013.

[3]Craig Walls.Spring in Action[M].America: Manning, 2015.

[4]Marten Deinum, Daniel Rubio, Josh Long.Spring 5 Recipes[M].America: Apress, 2017.

[5]Iuliana Cosmina, Rob Harrop, Chris Schaefer, Clarence Ho.Pro Spring 5[M].America: Apress, 2017.

[6]code4craft.tiny-spring[EB/OL].github.com/code4craft/…, 2018.

[7]DerekYRC.mini-spring[EB/OL].github.com/DerekYRC/mi…, 2021.

[8] 程序员囧辉.Spring IOC 源码学习 [EB/OL].blog.csdn.net/v123411739/…, 2019.

[9]Spring Team.Spring Core preference[EB/OL].docs.spring.io/spring-fram…, 2020.

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!