SpringBoot源码调试(四)---如何实现自动配置
# @SpringBootApplication注解
SpringBoot为何一个标注有@SpringBootApplication注解的启动类通过执行一个简单的run方法就能实现SpringBoot大量Starter的自动配置呢? 其实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标注了很多注解,我们可以看到其中跟SpringBoot自动配置有关的注解就有一个即@EnableAutoConfiguration,因此,可以肯定的是SpringBoot的自动配置肯定跟@EnableAutoConfiguration息息相关(其中@ComponentScan注解的excludeFilters属性也有一个类AutoConfigurationExcludeFilter,这个类跟自动配置也有点关系,但不是我们关注的重点)。 现在我们来打开@EnableAutoConfiguration注解的源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
看到@EnableAutoConfiguration注解又标有@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)两个注解,顾名思义,@AutoConfigurationPackage注解肯定跟自动配置的包有关,而AutoConfigurationImportSelector则是跟SpringBoot的自动配置选择导入有关(Spring中的ImportSelector是用来导入配置类的,通常是基于某些条件注解@ConditionalOnXxxx来决定是否导入某个配置类)。
因此,可以看出AutoConfigurationImportSelector类是我们本篇的重点,因为SpringBoot的自动配置肯定有一个配置类,而这个配置类的导入则需要靠AutoConfigurationImportSelector这个哥们来实现
# 寻找SpringBoot自动配置实现逻辑的入口方法?
AutoConfigurationImportSelector
的相关类图如下:
可以看到AutoConfigurationImportSelector
重点是实现了DeferredImportSelector
接口和各种Aware
接口,然后DeferredImportSelector
接口又继承了ImportSelector
接口。
自然而然的,我们会去关注AutoConfigurationImportSelector
复写DeferredImportSelector
接口的实现方法selectImports
方法,因为selectImports
方法跟导入自动配置类有关,而这个方法往往是程序执行的入口方法。经过调试发现selectImports
方法很具有迷惑性,selectImports
方法跟自动配置相关的逻辑有点关系,但实质关系不大。
最简单的方法就是在AutoConfigurationImportSelector
类的每个方法都打上断点,然后调试看先执行到哪个方法。但是我们可以不这么做,我们回想下,自定义一个Starter
的时候我们是不是要在spring.factories
配置文件中配置
EnableAutoConfiguration=XxxAutoConfiguration
因此可以推断,SpringBoot的自动配置原理肯定跟从spring.factories
配置文件中加载自动配置类有关,于是结合AutoConfigurationImportSelector
的方法注释,我们找到了getAutoConfigurationEntry
方法。于是我们在这个方法里面打上一个断点,此时通过调用栈帧来看下更上层的入口方法在哪里,然后我们再从跟自动配置相关的更上层的入口方法开始分析。
调试发现自动配置逻辑相关的入口方法在DeferredImportSelectorGrouping
类的getImports
方法处,因此我们就从DeferredImportSelectorGrouping
类的getImports
方法来开始分析SpringBoot的自动配置源码好了。
# 分析SpringBoot自动配置原理
既然找到ConfigurationClassParser.getImports()方法
是自动配置相关的入口方法,那么下面我们就来真正分析SpringBoot自动配置的源码了。
先看一下getImports
方法代码:
// ConfigurationClassParser.Java
public Iterable<Group.Entry> getImports() {
// 遍历DeferredImportSelectorHolder对象集合deferredImports,deferredImports集合装了各种ImportSelector,当然这里装的是AutoConfigurationImportSelector
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// 【1】,利用AutoConfigurationGroup的process方法来处理自动配置的相关逻辑,决定导入哪些配置类(这个是我们分析的重点,自动配置的逻辑全在这了)
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
// 【2】,经过上面的处理后,然后再进行选择导入哪些配置类
return this.group.selectImports();
}
标【1】
处的的代码是我们分析的重中之重,自动配置的相关的绝大部分逻辑全在这里了,将在4.1 分析自动配置的主要逻辑深入分析。那么this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector())
;主要做的事情就是在this.group
即AutoConfigurationGroup
对象的process
方法中,传入的AutoConfigurationImportSelector
对象来选择一些符合条件的自动配置类,过滤掉一些不符合条件的自动配置类,就是这么个事情,无他。
注:
AutoConfigurationGroup
:是AutoConfigurationImportSelector
的内部类,主要用来处理自动配置相关的逻辑,拥有process
和selectImports
方法,然后拥有entries
和autoConfigurationEntries
集合属性,这两个集合分别存储被处理后的符合条件的自动配置类,我们知道这些就足够了;AutoConfigurationImportSelector
:承担自动配置的绝大部分逻辑,负责选择一些符合条件的自动配置类;metadata
:标注在SpringBoot启动类上的@SpringBootApplication
注解元数据