前言
最深刻了解一个框架的思想的方式,莫过于看源码,本系列旨在于从Springboot底层源码(Version - 2.6.6)出发,一步步了解springboot是如何运行起来的。
从0-1了解SpringBoot如何运行(一):Environment环境装配
从0-1了解SpringBoot是如何运行起来的(二):定制你的banner
从0-1了解Spring是如何运行起来的(三):Context预处理,为加载容器做准备
在前述的文章中,我们主要了解了SpringBoot是如何实现配置文件的加载、context的创建、banner图打印的流程。这次的话我们主要针对context中的refresh处理进行学习和解析。
public ConfigurableApplicationContext run(String... args) {
......
refreshContext(context);
......
}
简介
本章节我们主要针对BeanDefinition的加载以及BeanFactory后处理进行学习。在展开讲解源码之前,我们需要先来了解一下这些概念。
BeanDefinition
BeanDefinition是一个描述 Bean的类实例,其其实是一个描述bean和修改bean信息的一个接口对象。如下是BeanDefinition的一个继承类图:
从类图中可以看到,其继承了BeanMetaDataElement
和AttributeAccessor
。前者是Bean的元数据的存储对象,其source属性保存的是Bean实例的最基础的定义信息。而AttributeAccessor
则是提供了对BeanDefinition的一些操作接口,包括获取特定的属性、移除特定属性以及添加特定属性的方法。
BeanDefinition在Spring中其实是一个相当重要的概念。其就如同Bean对象的图纸,需要生产什么样子的Bean都由BeanDefinition来定义。 同时结合类图来看,我们不难看到,BeanDefinition还有以下的一些能力:
- 标记当前的Bean的作用范围;
- 定义当前Bean的工厂Bean的名称;
- 当前Bean的初始化方法名称;
- 当前Bean是否支持懒加载
- ... ...
通过对BeanDefinition进行加载并存储到BeanDefinitionMap中,后续Spring才能依照这些对应的“图纸”生成出匹配的Bean实例对象。
BeanFactoryPostProcessor
BeanFactoryPostProcessor
实际是用来处理BeanFactory的后置处理器。其生效的主要时间段是在Bean实例化之前。另外,BeanFactoryPostProcessor
其实也是Spirng提供的一个钩子,其可以让你根据自己的实际情况修改Bean的定义属性。 最常见的应用就是我们的Bean中会有一些占位符,那么在Bean实例化之前这些占位符肯定是要被实际的配置参数填充的,这个填充的过程就是通过BeanFactoryPostProcessor
的后置处理完成的。
源码解析:
在了解了上述的基本内容后,我们就可以从源码角度对整个bean的加载过程进行了解和分析了。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// 对刷新做了一些初始化的设置
prepareRefresh();
// 更新对工厂的引用 并 获取容器工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 预先对BeanFactory进行相应的处理,如设置上下文的类加载器、默认的Bean等
prepareBeanFactory(beanFactory);
try {
// 抽象类预留的hook方法,允许子类在标准的BeanFactory初始化后,再进行初始化。(模版设计模式)
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
//关键代码:会在这里调用BeanFactory的后处理器来初始化BeanDefinition,BD会在这里被加载到BDMap中。
invokeBeanFactoryPostProcessors(beanFactory);
......
}catch (BeansException ex) {
...
}finally {
...
}
}
}
Refresh(代码的整体结构采用模版设计模式实现,具体的实现由子类实现。由于Refresh部分的整体的代码块相对较长,且包含的内容较多。因此,本期我们只针对invokeBeanFactoryPostProcessors()
及其之前的代码进行查看。可以看到,在执行invokeBeanFactoryPostProcessors()
之前,Spring对相应的工厂做了一些处理,主要包括
1、发送context刷新的事件;
2、更新工厂的引用数据;
3、预处理工厂数据,如设置累加载器,注入一些默认的BeanDefinition等。
4、如果这里我们实现了预留的postProcessBeanFactory
方法,那么还会执行这个模版钩子。
5、再次发送Bean初始化的事件
6、调用BeanFactory的后处理器。
这里我们跳过前面的几个步骤,直接追入最关键的源代码进行查看:
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
Set<String> processedBeans = new HashSet<>();
// 判断当前是否为BeanDef注册器
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}else {
regularPostProcessors.add(postProcessor);
}
}
//加载第一批次的BeanFactoryPostProcessor,并进行调用(该批次的均实现了PriOrityOrdered.class类)
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
//此处调用第一批次的BDF注册处理器。(如configClassPostProcessor,作用是加载所有的ConfigClass的BeanDef)
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
//调用第二批次的BDF处理器(此处代码中什么都没做。。。因为没有实现ordered的类 O。O)
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
//循环对第三批次的BDF处理器进行调用,直到无处理器,这里主要是对尚未初始化的BD进行占位符等信息的替换填充,
//eg. MapperScannerConfigurer会对mapper的基础包路径、作用域范围进行占位符的填充。
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
}
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}else {
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
//按照实现了PriorityOrder、Ordered和其余的分类方式将处理器分开
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
// 如果已经处理,则跳过
}else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// 首先,再次调用实现了priorityOrdered的Processor。
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// 接下来再调用一次实现了ordered的Processor。
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// 最后调用所有其他的后处理器
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// 清除掉所有的缓存数据信息
beanFactory.clearMetadataCache();
}
这部分的源码其实较长,整块代码主要是针对于BeanFactoryPostProcessor的执行进行操作的。这里梳理了一下整块代码的执行逻辑:
第一阶段:加载完BD前,调用初始化前的BeanFactoryPostProcessor:
- 调用实现了PriorityOrder的BeanFactoryPostProcessor类。(如ConfigurationClassPostProcessor,负责加载beanFactory内的BeanDef)
- 调用实现了Ordered的BeanFactoryPostProcessor类。
- 执行剩余的所有BeanFactoryPostProcessor类。(如MapperScannerConfigure,对mapper的基本包路径、是否懒加载等进行占位符替换。)
第二阶段: 加载完BD后,调用非提前初始化的 BeanFactoryPostProcessor:
调用实现了PriorityOrder的BeanFactoryPostProcessor类。(如PropertySourcesPlaceholderConfigurer,对所有的BeanDef的占位符进行填充)
调用实现了Ordered的BeanFactoryPostProcessor类。(如DependsOnDatabaseInitializationPostProcessor,控制DB的依赖属性)
执行剩余的所有BeanFactoryPostProcessor类。
首先,根据整个加载流程,可以比较直观的一点是,对于BeanFactory的处理,我们在某种程度上是可以控制其PostProcessor加载调用的先后顺序,这个可以通过PriorityOrder、Ordered等注解进行相应的控制。
接着,我们主要对上述的BeanFactoryPostProcessor中比较重要的两个处理器进行展开分析和学习:
ConfigurationClassPostProcessor
`org.springframework.context.annotation.ConfigurationClassPostProcessor`主要实现的功能是将各处的配置类及其对应的Bean的BeanDefinition扫描到对应的Factory 中。其执行的地方在第一阶段,加载的主要源代码如下所示:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
//获取此时的候选BeanDef的名字
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
} else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 如果当前没有需要加载的配置类,则直接返回
if (configCandidates.isEmpty()) {
return;
}
// 根据对应的@Order注解的值排序对应的配置候选类
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// 通过应用上下文来检测所有的自定义bean名称的生成策略。
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
//如果环境配置为null,则生成一个标准的环境
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// 生成一个配置类解析器
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
parser.parse(candidates); // 对配置的候选类进行解析,直到所有的配置类被解析完成,退出循环
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// 创建配置类BeanDefinition阅读器
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
//加载配置类BD
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();
candidates.clear(); // 清空候选配置类
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
上述源代码的主要工作流程如下所示:
1、因为整个类的主要功能其实是对配置的类进行加载,那么首先肯定要获取到哪些是我们当前需要加载的类,即找到configCandidates
(翻译为:候选的配置)
2、因为Spring本身加载配置类,也是需要一个先后顺序的,那么此时需要先根据@Order的方式来进行排序。
3、因为beanDefinition本身的名字都是由BeanNameGenerator
生成的,那么这时肯定需要找到哪个才是我们需要调用的Bean的名字的生成器,从而进行后续BeanDef的名字的生成
4、实例化一个 ConfigurationClassParser ,从名字猜测,就是专门解析我们的配置类的,然后执行parser.parse(candidates);
对候选配置类进行解析,在一开始的时候,beanDefinition里面除了那些约定的类,其实只有我们自己的启动类了,而我们的启动类也是有@Configuration的,所以一开始其实就是解析我们的启动类。在解析我们的启动类的时候,不难想象,application类底下肯定还是有许多依赖的类的(通常通过@ComponentScan扫描出来),那么自然也是需要对这些类进行加载的。这里我简单的画了一下不同配置类之间的依赖关系:
其实一看就是一颗典型的多叉树,那么对于多叉树的遍历,脑海中不由自主的其实会想到栈或递归。实际上,Spring也是如此做的。这个流程的大致加载逻辑如下图所示:
执行完了`parser.parse(candidates);`,其实只是将Config类的BeanDefinition给加载了,但是其中可能还有其管理的Bean实例可能都还是没有加载进来的的,那么此时就需要执行一次`this.reader.loadBeanDefinitions(configClasses);`对配置类管理的相关Bean进行加载。
在解析启动类之后,将解析出来的所有类再次减去已经解析的类,就会得到下一批需要解析的启动类,依次循环往返,直到解析完所有的类,那么此时加载就算完成了。
5、在加载完了所有的BeanDef后,会将元信息读取工厂的缓存清除了。由此以来,整个配置类及其依赖的BeanDef的加载流程就宣告结束。
PropertySourcesPlaceholderConfigurer
对于beanDef内占位符的替换,主要由*PropertySourcesPlaceholderConfigurer*实现。其主要的处理步骤如下所示:
1、依据当前工厂的不同environment创建不同的propertyResolver。
2、对不同的propertyResolver设置占位符的前缀(prefix)、后缀(suffix)、以及变量的切分符(valueSeparator)。一般默认会是"${"、"}"和":"
3、根据propertyResolver再创建出BeanDefinitionVisitor,用于对不同的bean进行遍历及变量替换。
替换变量逻辑中的关键的核心代码如下所示:
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
StringValueResolver valueResolver) {
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
//获取到所有已经加载好的Bd
String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
for (String curName : beanNames) {
// 首先判断不是处理“自己”对应的BD才会进行处理
if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
try {
//关键代码
visitor.visitBeanDefinition(bd);
}
catch (Exception ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
}
}
}
//在Spring2.5中会对Alias(同名属性)也进行更新
beanFactoryToProcess.resolveAliases(valueResolver);
//会将属性值的处理器保存下来
beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
}
执行前:
执行后:
总结
相比前几章,本章从内容来说,是Spring中相对关键关键的内容,主要描述的是关于BeanDef的加载、BeanDef内占位符的数据填充等。最后让我们回过头来,再次审视一次整个refresh()代码执行的逻辑流程:
1、发送context刷新的事件;
2、更新工厂的引用数据;
3、预处理工厂数据,如设置类加载器,注入一些默认的BeanDefinition等。
4、如果这里我们实现了预留的postProcessBeanFactory
方法,那么还会执行这个模版钩子。
5、再次发送Bean初始化的事件
6、调用BeanFactory的后处理器。
-
第一阶段:加载完BD前,调用初始化前的BeanFactoryPostProcessor:
- 调用实现了PriorityOrder的BeanFactoryPostProcessor类。(如ConfigurationClassPostProcessor,负责加载beanFactory内的BeanDef)
- 调用实现了Ordered的BeanFactoryPostProcessor类。
- 执行剩余的所有BeanFactoryPostProcessor类。(如MapperScannerConfigure,对mapper的基本包路径、是否懒加载等进行占位符替换。)
-
第二阶段: 加载完BD后,调用非提前初始化的 BeanFactoryPostProcessor:
调用实现了PriorityOrder的BeanFactoryPostProcessor类。(如PropertySourcesPlaceholderConfigurer,对所有的BeanDef的占位符进行填充)
调用实现了Ordered的BeanFactoryPostProcessor类。(如DependsOnDatabaseInitializationPostProcessor,控制DB的依赖属性)
执行剩余的所有BeanFactoryPostProcessor类。
归纳成流程图,整个流程就可以描述成如下的形式: