tips:下载源码,再结合本章内容,学习整个加载过程。
上一章,我们理解了 spring.factories 的触发时机,但放在 SpringBoot 的整个加载过程来讲,只能算部分。 而这一章,将从 SpringBoot 的加载全貌,进一步理解 Starter 的加载时机。
SpringBoot 的整个启动流程是比较复杂的,代码也较多;尽量抓住核心部分进行讲解。当然也不能对所有代码逐行进行分析, 坚持二八原则。
一、SpringBoot 启动过程
对于整个 SpringBoot 应用组成,可以分为两个部分。 一部分是 SpringBoot 核心基础,另外一部分是 spring framework 框架核心。通过这两部分组合,实现 SpringBoot 的加载启动
下图展示了 SpringBoot 启动过程高层次的组件图。 本文主要围绕图片中左边部分(SpringBoot加载过程)
二、 解析关键类
Spring Boot 启动的关键代表类。 ConfigurationClassParser 在整个 bean 启动过程中起到了十分关键的作用,它也是 Starter 与 SpringBoot 建立联系的桥梁。
各个类的描述
类 | 作用和描述 |
SpringApplication | Spring Boot 应用启动的主要入口点。它负责引导应用程序,创建并配置Spring上下文 |
SpringApplicationRunListeners | 监听器用于在应用程序的不同启动阶段接收事件通知;例如环境准备、上下文创建和应用程序启动等 |
ApplicationContext | 这是 Spring 框架的中心接口,代表了 Spring 容器。它管理应用程序中的bean定义、解析依赖关系等 |
BeanDefinition | 这是Spring中的一个核心概念,代表了 Spring 容器中的一个 bean 的定义,包括它的属性、构造器参数和具体的实现类等信息 |
ConfigurationClassPostProcessor | 特殊的 BeanFactory 后置处理器,用于处理 @Configuration 注解的类,从而读取和解析应用程序的配置。 |
ConfigurationClassParser | 用于解析@Configuration注解的类,分析@Bean方法以及@ComponentScan、@Import等注解。是最重要的解析类。 |
AutoConfigurationImportSelector | 实现 ImportSelector 接口,在 ConfigurationClassParser # processDeferredImportSelectors 处理 spring.factories 中的 Configuration 类,是自动装配、扩展的核心组件 |
SpringFactoriesLoader | 自定义spi的实现,读取spring.factories中的数据 |
BeanFactory | Spring的一个核心接口,它提供了高级别的bean工厂能力,用于管理和创建应用程序中的bean |
BeanDefinitionRegistry | 是一个接口,提供了注册 BeanDefinition 以及查询 BeanDefinition 的能力 |
关键核心类: ConfigurationClassPostProcessor、ConfigurationClassParser、SpringFactoriesLoader、ConfigurationClassPostProcessor 等几个类;其他几个是 bean 的实例化。
下面的时序图给出了关键位置。
三、 关键时序图
通过后置处理器 ConfigurationClassPostProcessor 调用 ConfigurationClassParser 来解析配置类,包括读取配置类中的注解信息,比如 @Bean
、@ComponentScan
、@Import
等,并将其转化为容器中管理的bean定义。
ConfigurationClassParser 是解析核心。
在 ConfigurationClassParser # processDeferredImportSelectors 将处理所有延迟的ImportSelectors
能够处理 spring.factories 中的 org.springframework.boot.autoconfigure.EnableAutoConfiguration
,是通过 importSelector # selectImports
返回 list 集合。而 importSelector 接口实现最重要的类则是AutoConfigurationImportSelector
四、桥梁纽带之AutoConfigurationImportSelector
org.springframework.context.annotation.ConfigurationClassParser#processDeferredImportSelectors
将是建立 spring.factories 与 Spring 容器之间的桥梁,而AutoConfigurationImportSelector
是整个自动装配的纽带。
AutoConfigurationImportSelector
是Spring Boot自动配置机制的关键组成部分。
- 自动配置类选择:
AutoConfigurationImportSelector
实现了Spring Framework中的ImportSelector
接口,它负责在Spring Boot应用程序启动过程中选择和激活一系列的自动配置类。 - 读取
spring.factories
: 它使用SpringFactoriesLoader
来读取在类路径中的META-INF/spring.factories
文件的全路径名。 AutoConfigurationImportSelector
通过AutoConfigurationImportFilter
接口实现的实例来过滤自动配置类,确保只有符合当前应用程序上下文条件的自动配置类被包含在内。
AutoConfigurationImportSelector
可以智能地应用对应的配置,从而为应用程序提供了很大的灵活性和便利。
关于 AutoConfigurationImportSelector
的引入过程如下:
@SpringBootApplication > @EnableAutoConfiguration > @Import(AutoConfigurationImportSelector.class)
关于 AutoConfigurationImportSelector 类图情况
通过 AutoConfigurationImportFilter 的使用,AutoConfigurationImportSelector 可以精确地控制哪些自动配置类应被激活,以及哪些应被排除,确保了自动配置的灵活性和准确性。
实现了 DeferredImportSelector 的 接口, Spring 调用其 selectImports() 方法。其中 DeferredImportSelector 继承了 ImportSelector,DeferredImportSelector 实例的 selectImports() 方法的调用时机晚于 ImportSelector 实例, 等到 @Configuration 注解中相关的业务全部都处理完了才会调用
AutoConfigurationImportSelector: 实现 ImportSelector 接口,负责读取spring.factories文件,并将符合条件的配置类名作为候选配置返回给Spring容器。
SpringFactoriesLoader: 用于加载 spring.factories 文件中指定的工厂名。
读取过程如下所示:
五、全局解析时序图
图一、重点关注 springBoot 应用启动过程,以及 springBoot 和 ConfigurationClassParser 之间的时序。
图二、重点关注 spring.factories 的加载以及 和 AutoConfigurationImportSelector 之间的关系
图三、重点关注 ConfigurationClassParser 解析过程
特别说明:解析是一个递归过程,下面的时序图只能表示逻辑思想,不是严格的顺序。
六、本章小结
- SpringBoot Application 包含两个核心部分:一部分 SpringBoot, 一部分 Spring framework core
- 其中最核心关键的是 ConfigurationClassParser,
processDeferredImportSelectors
是 Starter 与 Spring 容器核心的桥梁。
到这里,对整个 Starter 的解析过程,已经有了一个大概的理解,接下来,我们会再深入地分析自动装配过程和 ConfigurationClassParser 源码解析。
七、延伸阅读 configClasses -> BeanDefinition
上面只是解析 ConfigurationClass,将其变成 Spring容器(DefaultListableBeanFactory)中的 BeanDefinition。 还需 ConfigurationClassBeanDefinitionReader 等处理。
关键代码 org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
....
do {
parser.parse(candidates);
...
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
// 将 configClasses 变成 BeanDefinition
this.reader.loadBeanDefinitions(configClasses);
...
}
while (!candidates.isEmpty());
...
}
执行之前 this.reader.loadBeanDefinitions(configClasses);
此时 DefaultListableBeanFactory 中数量为 10
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(256);
执行以后。 BeanDefinition 从 10 变成 162。将所有相关 configuration 中符合的 bean 都解析成 BeanDefinition,并放入到 DefaultListableBeanFactory
注:对于 ConfigurationClassBeanDefinitionReader 的处理逻辑,可自行研究; BeanDefinition 目前还不是 class 类,还需要一系列的属性填充、初始化等过程。
特别说明, BeanDefinition 的类型,根据加载的来源方式还略有不同。
在这个部分中,我们能够明白目前还不是真正的 bean, 只是 configClasses。
已同步发布到公众号:面汤放盐 第五节 Starter 的加载全貌 (qq.com)
掘金账号: 第五节 Starter 的加载全貌 - 掘金 (juejin.cn)