盘点 SpringBoot : 自动装配
时间:2023-05-16 14:37:01
一 . 前言
无意中把 自动了自动组装的相关源码, 所以我终于可以打开这个系列了.
让我们来看看这个系列的自动组装过程 , 整个过程从一年前开始学习 , 没有找到合适的表达形式 , 最近灵感爆发 , 试试这个方法.
二 . 背景知识点
2.1 基础背景
Spring Boot 根据添加的自动配置尝试自动配置 jar 依靠项自动配置 Spring 应用程序 , Spring 允许更换自动装配 , 使Spring 使用更灵活.
当然 , 配置不可能无缘无故产生 , 打开 Spring-boot-autoconfiture 您可以看到相应的配置文件 :
- spring.factories : 自动装配类 , Spring 启动时使用 ,我们后面分析
- spring-autoconfigure-metadata.properties : 配置信息
- spring-configure-metadata.json : 配置元数据 , 业务将通过该文件进行属性映射和处理
-
例如 : spring.jpa.hibernate.ddl-auto 提供多种模式
-
{ "name": "spring.jpa.hibernate.ddl-auto", "values": [ { "value": "none", "description": "Disable DDL handling." }, { "value": "validate", "description": "Validate the schema, make no changes to the database." }, { "value": "update", "description": "Update the schema if necessary." }, { "value": "create", "description": "Create the schema and destroy previous data." }, { "value": "create-drop", "description": "Create and then destroy the schema at the end of the session." } ] } 复制代码
自动装配和自动配置
- 自动配置:是的 Spring Boot 提供,实现通过 jar 可自动配置应用程序。
- 例如:我们介绍 spring-boot-starter-web 之后,自动引入 Spring MVC 相关的 jar 自动配置包 Spring MVC 。
- 自动组装:是的 Spring 提供的 IoC 具体看注入方式 《Spring 教程 —— Beans 自动装配》 文档。
2.2 基础使用
让我们来看看基础的使用 :
@Configuration @EnableAutoConfiguration public class BeanAnnotationConfig { @Bean public BeanAnnotationTestService getBeanAnnotationTestService() { return new BeanAnnotationTestService(); } } 复制代码
实现基础 : 基于自动装配 @SpringBootApplication 注解实现
F- SpringBootApplication |- @SpringBootConfiguration: 这是一个标记 Spring Boot 配置类 |- @EnableAutoConfiguration : 开启自动配置功能 |- @AutoConfigurationPackage: 获取主程序类所在的包路径,并在包路径(包括子包)下注册所有组件 Spring IOC 容器中 |- @Import : 导入资源(并由AutoConfigurationImportSelector) |- @ComponentScan(内部为excludeFilters ) 复制代码
三. 源码分析
尝试一种新的方法来分析源代码 :
3.1 扫描的起点
3.2 业务扫描关系
先看基本逻辑 :
1. 由 ConfigurationClassPostProcessor 开始
2. 在 ConfigurationClassPostProcessor 的 doProcessConfigurationClass 获得一切 config 类信息 , 其中就包括 @Bean 标注的Bean
3. 最终在 ConfigurationClassBeanDefinitionReader 的 loadBeanDefinitionsForBeanMethod 中进行加载
C01- ConfigurationClassPostProcessor : 处理 processConfigBeanDefinitions 类型
M101M102M102 doWhile调用do-while parse 循环 处理ConfigurationClassParser 处理ConfigurationClassBeanDefinitionReader 处理判断已经处理生成新的未处理判断candidates 为空 ,处理完成将ImportRegistry注册为一个bean清除外部提供的MetadataReaderFactory中的缓存M101M102M102 doWhile
C01- ConfigurationClassPostProcessor M101- postProcessBeanDefinitionRegistry - System.identityHashCode(registry) 获取一个 registryId - 将 id 加入 Set - processConfigBeanDefinitions(registry) : 详看 M102 M102- processConfigBeanDefinitions - 从 BeanDefinitionRegistry 获取一个 List 集合 ?- 通过每个 BeanDefinition 的 Attribute是否为 configurationClass , 加入集合 ?- 此处是将 BeanDefinitionRegistry 添加获得的所有类过滤器 - 通过 Order 排序一次 - 获取相关 Generator ,通过这些 Generator 获取一个 ConfigurationClassParser - List 封装为一个 Set , 保证其独特性 FOR- 循环所有的 BeanDefinitionHolder - 通过 ConfigurationClassParser 处理该集合 - 通过 ConfigurationClassBeanDefinitionReader load configClasses // M01 伪代码 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { int registryId = System.identityHashCode(registry); //... this.registriesPostProcessed.add(registryId); processConfigBeanDefinitions(registry); } // M02 伪代码 public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { // Step 1 : 获取 configurationClass 集合 List configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames(); // ... 这里做出了相关判断 configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); // Step 2 : 排序 configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefintion());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// 省略 : 生成 ConfigurationClassParser 的必要参数
// Step 3 : 准备一个 ConfigurationClassParser
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
// 准备所有的 ConfigClass 的 BeanDefinitionHolder 集合
Set candidates = new LinkedHashSet<>(configCandidates);
Set alreadyParsed = new HashSet<>(configCandidates.size());
do {
// Step 4 : parse 处理
parser.parse(candidates);
parser.validate();
// 准备 ConfigurationClass 并且去掉已经处理的
Set configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Step 5 : reader 处理
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
}while (!candidates.isEmpty());
// 将ImportRegistry注册为一个bean,以便支持importtaware @Configuration类
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// 清除外部提供的MetadataReaderFactory中的缓存
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
复制代码
C02- ConfigurationClassParser : 主处理逻辑
ConfigurationClassParser 主要用于解析单个配置类 . 但是因为 @Import 注解的原因 , 可能会产生多个ConfigurationClass
M101M102M202M203M204调用FOR 循环处理 SourceClass调用 processConfigurationClass调用 doProcessConfigurationClass循环处理 PropertySources循环处理 ComponentScans递归处理 @Bean method递归处理 @Import获取超类返回超类或者null递归处理超类将处理类加入 Map 集合M101M102M202M203M204
C02- ConfigurationClassParser
M201- parse(Set configCandidates)
- 循环 Set , 根据其不同类型调用不同的parse
- this.deferredImportSelectorHandler.process();
?- 调用内部类 DeferredImportSelectorHandler 处理相关的逻辑
M202- processImports
- this.importStack.push(configClass) :
// 从这里之后开始核心的 Class 类处理逻辑 , 会分别判断 SourceClass 的类型
FOR- Collection : for 循环传入的集合 (Collection - Group.Entry.getImportClassName) , 获取 SourceClass
1- TODO
2- TODO
3- 除了以上2种外
- processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter) : M203
M203- processConfigurationClass
- asSourceClass(configClass, filter) : 获取 SourceClass
- sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter) : M204
- TODO 待完善
M204- doProcessConfigurationClass
- 如果类注解是 Component , 调用 processMemberClasses(configClass, sourceClass, filter)
- For 循环 其上 PropertySources 注解 , 并调用 processPropertySource(propertySource)
- For 循环 其上 ComponentScans 注解
- this.componentScanParser.parse 获取 Set
FOR- Set
1- 获取 BeanDefinition
2- 如果检查给定的bean定义是配置类的候选 , 调用 parse(bdCand.getBeanClassName(), holder.getBeanName())
?- 这里可以看成是一个递归处理
- 调用 processImports 处理 @Import 注解对应的类
?- 这其实也是一个递归处理
- 获取 ImportResource 对应的属性
IF- 如果对应属性不为空
- importResource.getStringArray("locations") : 获取其中的 locations 属性 String[]
- 获取 BeanDefinitionReader
FOR- 循环数组
- configClass.addImportedResource(resolvedResource, readerClass)
- retrieveBeanMethodMetadata(sourceClass) : 获取其中 @Bean 对应Method
FOR- 循环上面的 Set
- configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
- processInterfaces(configClass, sourceClass) : 处理其中 interface default 方法
- 获取父类型 , 再将其添加到 Map 中
- return sourceClass.getSuperClass() : 找到超类,返回其注释元数据并递归
M05- processMemberClasses
复制代码
C03- DeferredImportSelectorHandler
C03- DeferredImportSelectorHandler
M301- process() :
- 将 List 排序后注入到 DeferredImportSelectorGroupingHandler 中
- 调用 handler.processGroupImports() 处理 所有的 Group
复制代码
C04- DeferredImportSelectorGroupingHandler
M301M401M501获取所有Group 后调用 processGroupImports 处理 Group获取 Group process 处理总逻辑 中 DeferredImportSelectorHolder执行 Group process 处理总逻辑M301M401M501
C04- DeferredImportSelectorGroupingHandler
M401- processGroupImports
- 先循环 DeferredImportSelectorGrouping
- grouping.getImports() : 返回 Iterable
- 获取 Group 中 DeferredImportSelectorHolder
- DeferredImportSelector.Group 处理 (process) 每一个 DeferredImportSelectorHolder (C05 逻辑)
FOR- 其次循环每个 DeferredImportSelectorGrouping 的 Imports
- this.configurationClasses.get(entry.getMetadata()) : 获取当前 ConfigurationClass
- processImports 处理 , 详见 processImports 方法逻辑 (M202)
复制代码
C05- AutoConfigurationImportSelector
从上一步中 , 其中一个 DeferredImportSelectorHolder 就是 AutoConfigurationImportSelector
M401M501M502M503调用通过 AnnotationMetadata 获取 AutoConfigurationEntry获取所有的 configurations 列表过滤重复及filter 过滤需要跳过的ConfigClass处理 listener 和 event返回一个 AutoConfigurationEntryAutoConfigurationEntry 加入 Map
C05- AutoConfigurationImportSelector
M502- getAutoConfigurationEntry
- getAttributes(annotationMetadata) : 获取 AnnotationMetadata 中属性
- getCandidateConfigurations(annotationMetadata, attributes) : 获取所有的 configurations 列表
- removeDuplicates : 移除重复的配置类
- checkExcludedClasses + removeAll : 移除 getExclusions(annotationMetadata, attributes) 获取的类
- getConfigurationClassFilter().filter(configurations)
- filter.match(candidates, this.autoConfigurationMetadata) : filter 过滤是否跳过
- fireAutoConfigurationImportEvents(configurations, exclusions) : 详见 fireAutoConfigurationImportEvents
- new AutoConfigurationEntry(configurations, exclusions) : 返回一个 AutoConfigurationEntry
M503- fireAutoConfigurationImportEvents
- getAutoConfigurationImportListeners() : 获取AutoConfigurationImportListener集合
- listener.onAutoConfigurationImportEvent(event) :分别通过每个 listern , 处理自动配置导入事件
C05PSC01- AutoConfigurationGroup
?- 该类为内部静态类
M501- process :
- AutoConfigurationImportSelector.getAutoConfigurationEntry(annotationMetadata) : 获取一个 AutoConfigurationEntry 对象
?- 详见 getAutoConfigurationEntry 方法
- this.autoConfigurationEntries.add(autoConfigurationEntry) : 添加到 AutoConfigurationEntry 集合中
FOR- List : For 循环整个 集合
- this.entries.putIfAbsent(importClassName, annotationMetadata) : 放入 Map
?- 这里意味着将所有的 AutoConfigurationEntry 重新处理成了 Map
// M501 伪代码
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
// 调用 M502 生成 AutoConfigurationEntry
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
// 循环处理加入 放入 Map
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
// M502 核心伪代码
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 移除和过滤
configurations = removeDuplicates(configurations);
Set exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
// 调用处理 Listener
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
复制代码
C06- ConfigurationClass
从 C02-M204 中 ,我们可以看到 , 类上注解以及类中 Bean 都被配置到了 ConfigurationClass , 我们看看相关逻辑是什么样的
C06- ConfigurationClass
- getBeanMethods() : 观察后发现其被调用的地方是 ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass
复制代码
C07- ConfigurationClassBeanDefinitionReader
C06M701M702M703加载 BeanDefinitions循环所有的的 @Bean 注解Method如果是 Imported , 调用 702 注册For 循环 loadBeanDefinitionsForBeanMethod判断@Bean @Lazy 等注解处理 autowire 等方法this.registry.registerBeanDefinition 注册C06M701M702M703
C07- ConfigurationClassBeanDefinitionReader
M- loadBeanDefinitions
M701- loadBeanDefinitionsForConfigurationClass
- 判断当前 configClass 是否应该跳过
true : 从相关对象中移除
- this.registry.removeBeanDefinition(beanName)
- this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName())
- 如果是 Imported , 调用 registerBeanDefinitionForImportedConfigurationClass (M702)
FOR- 循环处理 configClass.getBeanMethods() 获得的Bean 方法
- loadBeanDefinitionsForBeanMethod(beanMethod) :M703
M702- registerBeanDefinitionForImportedConfigurationClass
- configClass.getMetadata() : 获取 AnnotationMetadata
- 通过 AnnotationMetadata 生成 AnnotatedGenericBeanDefinition
- 通过 AnnotatedGenericBeanDefinition 生成 ScopeMetadata , 并且为 AnnotatedGenericBeanDefinition 设置 Scope
- 通过 BeanNameGenerator 生成一个 Config BeanName
- 创建一个 BeanDefinitionHolder , 并且 this.registry.registerBeanDefinition 注册
- configClass.setBeanName(configBeanName) : 为 Class 命名
M703- loadBeanDefinitionsForBeanMethod(beanMethod) : 加载 Bean 主逻辑 , 此处已经到了方法处理
- beanMethod.getConfigurationClass() : 获取当前的 ConfigurationClass
- beanMethod.getMetadata() : 获取 MethodMetadata , 该元数据包含当前方法的信息
- 如果当前方法应该跳过 , 则加入到 configClass.skippedBeanMethods , 并且返回
- AnnotationConfigUtils.attributesFor(metadata, Bean.class) : 获取当前方法上 Bean 注解的属性
- bean.getStringArray("name") 获取名称集合 , 取第一个为Bean 名 , 其他的作为别名
- ????
- 通过 configClass 和 metadata 构建一个 ConfigurationClassBeanDefinition
- 为 ConfigurationClassBeanDefinition 设置 extractSource
?- configClass.getResource()
IF- 判断是否为标注 @Bean 的 静态方法
true:
false: 设置 FactoryBeanName 和 UniqueFactoryMethodName
- beanDef.setFactoryBeanName(configClass.getBeanName()) : 设置了当前 config bean 的名称
- beanDef.setUniqueFactoryMethodName(methodName) : 当前 method 的名称
- beanDef.setAutowireMode :设置注入的模式 , 此处是 AUTOWIRE_CONSTRUCTOR
?- AUTOWIRE_NO / AUTOWIRE_BY_NAME / AUTOWIRE_BY_TYPE / AUTOWIRE_CONSTRUCTOR / AUTOWIRE_AUTODETECT
- beanDef.setAttribute : 设置 skipRequiredCheck 为 true
- AnnotationConfigUtils.processCommonDefinitionAnnotations : 设置加载类型
?- 包括 Lazy , Primary , DependsOn , Role , Description
- bean.getEnum("autowire") + beanDef.setAutowireMode(autowire.value())
- bean.getBoolean("autowireCandidate") + setAutowireCandidate
- bean.getString("initMethod") + beanDef.setInitMethodName(initMethodName)
- bean.getString("destroyMethod") + beanDef.setDestroyMethodName(destroyMethodName)
- AnnotationConfigUtils.attributesFor(metadata, Scope.class) : 获取 + beanDef.setScope 配置 Scope 属性
- attributes.getEnum("proxyMode") : 获取 proxyMode
- ScopedProxyCreator.createScopedProxy : 通过获取的 proxyMode + registry + beanDef 等构建 BeanDefinitionHolder
- 通过 BeanDefinitionHolder + configClass + metadata 构建 ConfigurationClassBeanDefinition
- this.registry.registerBeanDefinition(beanName, beanDefToRegister) : 最后注册 Bean
?- registry 是一个 BeanDefinitionRegistry
复制代码
总结
这是一篇测试型的文档 , 想测试一下该以什么样的形式来展现源码流程 , 也不知道这篇文档有没有达到效果.
后续还会对整篇文章继续处理 , 争取找到更合适的模式.
自动装配属于笔记里面比较早期的文章 , 本地还有很多 IOC , Cloud 的文章 , 找到合适的模式后应该能很好的展现了!!
作者:AntBlack
链接:https://juejin.cn/post/6949373202900451364
来源:掘金