springboot启动流程(十)springboot自动配置机制
所有文章
https://www.cnblogs.com/lay2017/p/11478237.html
正文
在第七篇文章中我们了解到,refresh过程将会调用ConfigurationClassPostProcessor这个后置处理器,而这个后置处理器将会去调用ConfigurationClassParser这个配置类的解析器,而第一个被解析的配置类就是我们main方法所在的主类(主类是在refresh之前的,prepareRefresh方法加载成为BeanDefinition到BeanFactory中的)。
而后,在第八篇文章中我们主要看了看ConfigurationClassParser是怎么解析配置类的@ComponentScan这个注解的。
那么本文将继续从ConfigurationClassParser这个过程开始,看看parse的处理过程关于自动配置的内容。
自动配置入口
首先,我们回到ConfigurationClassParser的parse方法。
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
// 解析主类的入口
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
} else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
} else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
} catch (BeanDefinitionStoreException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
// 处理自动配置的入口
this.deferredImportSelectorHandler.process();
}
springboot解析过程将从解析main方法所在的主类开始,所以我们先跟进parse方法
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName));
}
再跟进processConfigurationClass方法
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
// 判断当前配置类是否应该跳过解析
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// 省略
// 向父类递归解析
SourceClass sourceClass = asSourceClass(configClass);
do {
// 解析当前配置类的核心逻辑
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
//
}
这里先做了一个判断,是否跳过当前配置类的解析(后面会提及)。而后就是对配置类的递归解析,如果有父类将会递归解析。
跟进doProcessConfigurationClass方法,我们省略其它内容
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException { // // 处理@Import注解
processImports(configClass, sourceClass, getImports(sourceClass), true); //
return null;
}
我们看到,解析逻辑中包含着一个processImports方法,用于处理@Import注解。我们知道,每一个springboot程序将会注解一个@SpringBootApplication注解,这个注解是一个组合注解,我们看看该注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
// 省略
}
@SpringBootApplication注解组合了@EnableAutoConfiguration注解,我们再看看@EnableAutoConfiguration注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
// 省略
}
可以看到,@Import注解导入了一个AutoConfigurationImportSelector类。
我们再回到doProcessConfigurationClass方法
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException { // // 处理@Import注解
processImports(configClass, sourceClass, getImports(sourceClass), true); //
return null;
}
processImports之前,会调用getImports方法获取当前配置类的@Import,也包含组合的注解。所以@SpringBootApplication组合的@Import注解导入的配置类AutoConfigurationImportSelector将在这里被获取。
我们跟进getImport方法看看
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<>();
Set<SourceClass> visited = new LinkedHashSet<>();
collectImports(sourceClass, imports, visited);
return imports;
}
再跟进collectImports看看是怎么搜集类的
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException { if (visited.add(sourceClass)) {
// 获取所有注解
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
// 非@Import注解的,递归看看有没有组合@Import注解
if (!annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
很显然,搜集过程将对所有注解递归处理,这样我们就获得了main方法所在主类的所有@Import导入的类。
再回到doProcessConfigurationClass方法
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException { // // 处理@Import注解
processImports(configClass, sourceClass, getImports(sourceClass), true); //
return null;
}
跟进processImports方法
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
// if (checkForCircularImports && isChainedImportOnStack(configClass)) {
//
}
else {
//
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
//
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
//
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
//
}
else {
//
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
//
}
}
这里遍历了我们getImports方法获取到的类,但是目前我们还只有AutoConfiguratonImportSelector这个类。这个类实现了DeferredImportSelector接口,所以我们继续跟进handle方法
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
if (this.deferredImportSelectors == null) {
//
} else {
// 添加到集合
this.deferredImportSelectors.add(holder);
}
}
AutoConfigurationImportSelector被包装已经添加到了集合中。那么这个被添加到集合中的类是什么时候被处理的呢?
我们回到本文最开始的代码片段,parse方法
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
// 解析主类的入口
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
} else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
} else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
} catch (BeanDefinitionStoreException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
// 处理自动配置的入口
this.deferredImportSelectorHandler.process();
}
可以看到,parse方法的最后一行,调用了process方法,将会对这些类进行处理,这也就是自动配置的入口方法了。
处理AutoConfigurationImportSelector
我们跟进process方法,看看处理过程
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
// 遍历导入的类,将这些类注册到handler中
deferredImports.forEach(handler::register);
// 处理handler中的导入类
handler.processGroupImports();
}
} finally {
//
}
}
这里先将导入类调用handler的register方法进行注册,然后集中处理。我们稍微瞄一眼register方法
public void register(DeferredImportSelectorHolder deferredImport) {
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent((group != null ? group : deferredImport), key -> new DeferredImportSelectorGrouping(createGroup(group)));
// 添加到group中
grouping.add(deferredImport);
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass());
}
跟进add方法
private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>();
public void add(DeferredImportSelectorHolder deferredImport) {
this.deferredImports.add(deferredImport);
}
添加到一个集合中存放起来
我们回到process方法
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
// 遍历导入的类,将这些类注册到handler中
deferredImports.forEach(handler::register);
// 处理handler中的导入类
handler.processGroupImports();
}
} finally {
//
}
}
register完毕以后,将会processGroupImports,我们跟进processGroupImports方法
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
// 获取所有AutoConfigurationImportSelector返回的待处理的配置类,并遍历
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(
entry.getMetadata());
try {
// 处理所有配置类
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
AutoConfigurationImportSelector需要进行自动配置的类将会在这里的getImports方法中返回,而后processImports方法将会处理所有这些需要自动配置的类
我们先跟进getImports方法,看看是怎么获取所有待处理的配置类的
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// 处理生成结果
this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector());
}
// 返回结果
return this.group.selectImports();
}
我们主要看process方法,进入到AutoConfigurationGroup的process方法
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
再跟进getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
//
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// // 过滤
configurations = filter(configurations, autoConfigurationMetadata);
//
return new AutoConfigurationEntry(configurations, exclusions);
}
继续跟进getCandidateConfigurations
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
//
return configurations;
}
我们看到一个熟悉的方法loadFactoryNames(不熟悉的话,请阅读辅助内容),看看getSpringFactoriesLoaderFactoryClass返回什么
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
我们可以随机打开一个spring.factories看看EnableAutoConfiguration作为key的配置

可以看到spring.factories中将需要进行自动配置的类作为value配置在这里,所以getCandidateConfigurations方法将会把这些配置类返回。我们再回到getAutoConfigurationEntry方法
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
//
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// // 过滤
configurations = filter(configurations, autoConfigurationMetadata);
//
return new AutoConfigurationEntry(configurations, exclusions);
}
获取完configurations后,将会进行一次过滤操作,这样可以避免大量的不需要配置的类被加载。
再回到AutoConfiguration的process方法
private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
我们调用getAutoConfigurationEntry获得需要自动配置的类,然后再这里会被添加到一个Map集合中存放起来。
到这里,AutoConfigurationImportSelector的getImports方法的process过程就结束了。我们回到getImports方法
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// 处理生成结果
this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector());
}
// 返回结果
return this.group.selectImports();
}
process方法获取了Imports,而selectImports将返回结果。
到这里,我们的getImports方法就获取了可能需要进行自动配置的类,回到DeferredImportSelectorGroupingHandler类的processGroupImports方法
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
// 获取所有AutoConfigurationImportSelector返回的待处理的配置类,并遍历
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(
entry.getMetadata());
try {
// 处理所有配置类
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
跟进processImports
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
//
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
//
}
else {
//
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
//
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
//
}
else {
// 处理配置类
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
//
}
}
导入的类作为配置类来处理,跟进processConfigurationClass方法
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
// 是否要进行配置解析
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
//
SourceClass sourceClass = asSourceClass(configClass);
do {
// 解析的逻辑
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
//
}
我们久违的processConfigurationClass方法,一开始我们关注的是doProcessConfigurationClass看它解析过程的。现在我们来看看shouldSkip方法,看看是怎么判断当前配置类是否要进行解析的。
跟进shouldSkip方法
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
//
List<Condition> conditions = new ArrayList<>();
// 获取配置类中所有@Conditional以及组合@Conditional的条件
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
Condition condition = getCondition(conditionClass, this.context.getClassLoader());
conditions.add(condition);
}
}
//
// 遍历这些条件
for (Condition condition : conditions) {
ConfigurationPhase requiredPhase = null;
if (condition instanceof ConfigurationCondition) {
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
}
// 判断条件是否不匹配
if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
return true;
}
}
return false;
}
shouldSkip方法,将会拿到当前配置类的所有@Conditional或者组合了@Conditional的注解,并将注解生成Condition条件,再遍历这些条件看是否有不满足条件的将返回true。
总结
springboot的自动配置将从解析main方法所在的主类开始,ConfigurationClassParser在解析@Import的时候会获取到AutoConfigurationImportSelector类。AutoConfigurationImportSelector将会获取到所有可能需要进行自动配置的类,而后每个配置类将被和main方法所在的主类一样准备解析,在解析之前会根据像@Conditional或者组合@Conditional的注解来生成判断条件Condition,根据是否满足Condition来决定是否要进行自动配置。
springboot启动流程(十)springboot自动配置机制的更多相关文章
- springboot启动流程(十二)springboot事务自动配置
所有文章 https://www.cnblogs.com/lay2017/p/11478237.html 正文 在上一篇文章中,我们简单了解了aop的处理过程.代理增强之前,先生成Advisor,然后 ...
- SpringBoot启动流程分析(五):SpringBoot自动装配原理实现
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(六):IoC容器依赖注入
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(二):SpringApplication的run方法
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程分析(四):IoC容器的初始化过程
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- SpringBoot启动流程及其原理
Spring Boot.Spring MVC 和 Spring 有什么区别? 分别描述各自的特征: Spring 框架就像一个家族,有众多衍生产品例如 boot.security.jpa等等:但他们的 ...
- SpringBoot启动流程解析
写在前面: 由于该系统是底层系统,以微服务形式对外暴露dubbo服务,所以本流程中SpringBoot不基于jetty或者tomcat等容器启动方式发布服务,而是以执行程序方式启动来发布(参考下图ke ...
- SpringBoot启动流程分析(一):SpringApplication类初始化过程
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- springboot启动流程(一)构造SpringApplication实例对象
所有文章 https://www.cnblogs.com/lay2017/p/11478237.html 启动入口 本文是springboot启动流程的第一篇,涉及的内容是SpringApplicat ...
- SpringBoot 启动流程
SpringBoot 启动流程 加载 resources/META-INF/spring.factories 中配置的 ApplicationContextInitializer 和 Applicat ...
随机推荐
- mybatis 的 <![CDATA[ ]]> 标签作用
有个细节一直没有注意到,今天mark一下: mybatis进行时间比较的时候我们会这么写,一直都是在复制粘贴所以没有注意,一个标签<![CDATA[ ]]> 今天在做另外一个有时间比较sq ...
- Linux下-bash: Permission denied 或者 sudo: command not found 错误
有时候执行一个脚本或者运行一个可执行文件时,如执行脚本./foo.sh,会报错-bash: ./foo.sh: Permission denied,你会再试sudo ./foo.sh,发现继续报错su ...
- 从成员函数指针生成可调用对象:function<>、mem_fn()和bind()
我们知道,普通函数指针是一个可调用对象,但是成员函数指针不是可调用对象.因此,如果我们想在一个保存string的vector中找到第一个空string,不能这样写: vector<string& ...
- Efcore迁移
Efcore迁移 Add-Migration XX:1.根据模型的实际结构对比当前快照,从而生成新迁移文件的Up和Down方法2.根据模型的实际结构修改快照和新迁移文件---------------- ...
- 泰乐事(Telos)白皮书中文版 <零> 封面及目录
<泰乐事白皮书> 一个可持续发展的去中心化EOSIO网络 作者:道格拉斯·合恩 泰乐事(Telos)—— 事物的终极目标. Telos一词来源于希腊语ΤΈΛΟΣ. “一颗橡果的终极目标是成 ...
- Composer 笔记
composer 依赖于git而设计的代码仓管理工具 1.可以通过手动配置源,获取代码 "require": { "group/val": "0.0. ...
- select 和v-model
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- [转帖]APP逆向神器之Frida【Android初级篇】
APP逆向神器之Frida[Android初级篇] https://juejin.im/post/5d25a543e51d455d6d5358ab 说到逆向APP,很多人首先想到的都是反编译,但是单看 ...
- 第二坑:Linux发布项目
在这里踩过两个坑: 1.第一个是上传文件的时候,不知道是什么原因,上传失败了,然后发布新版本的时候,依然用的上个版本的包,导致工作出现重大失误. 谨记:上传文件的时候,检查一下上传文件和本地文件的时间 ...
- IP地址 子网掩码 网络地址 主机地址 广播地址
1.一定要明白各自的概念分别表示什么 IP地址:IP地址是用来识别网络上的设备,因此,IP地址是由网络地址与主机地址两部分所组成. 子网掩码:子网掩码不能单独存在,它必须结合IP地址一起使用.子网掩码 ...