在 【Java的SPI与SpringBoot自动配置】中我们知道了springBoot的自动配置是参考java的SPI机制的实现。
下面我就一起看下具体的实现流程,到底是如何读取约定的配置文件又是如何自动配制的。
1、读取配置(约定的配置文件)
先来一张流程图, 参照的看会更清晰
首先入口大家应该都知道, 就是主启动类上的@SpringBootApplication注解
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
看源码, 这是一个复合注解, 跟配置有关的有两个注解
- @SpringBootConfiguration
作用是将当前启动类(就是上面的DemoApplication)标记为配置类 - @EnableAutoConfiguration
无疑, 就是这个注解实现的自动配置了
@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 {
}
继续追踪@EnableAutoConfiguration 注解, 又是一个复合注解
- @AutoConfigurationPackage
主要作用是用@Import的方式注入了AutoConfigurationPackages.Registrar类, 这个类主要作用是将启动类的类路径用BasePackages类对象包装了一下(jpa有用到) - @Import(AutoConfigurationImportSelector.class)
这个类实现了DeferredImportSelector和ImportSelector接口, 下面我把核心代码抽出来
如果实现了
DeferredImportSelector#getImportGroup
方法
就执行AutoConfigurationGroup#process读取配置, 追踪路径如下:
getAutoConfigurationEntry
- getCandidateConfigurations
---- loadFactoryNames
---- loadSpringFactories
从META-INF/spring.factories文件读出所有配置类, 格式为Map<String, LIst< String>>
---- getOrDefault
根据EnableAutoConfiguration.class从map中获取自动配置类
- filter
根据@Conditional进行过滤
否则就会执行ImportSelector#selectImports
方法(spring主要走这个逻辑)
读取一批类全限定类名称,去生成bean
(SpringBoot 3.x之后不再读META-INF/spring.factories文件, 改从META-INF/spring/目录下org.springframework.boot.autoconfigure.EnableAutoConfiguration.imports文件中读取)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
//具体看下AutoConfigurationImportSelector类的实现
public class AutoConfigurationImportSelector implements DeferredImportSelector... {
//实现ImportSelector方法
@Override
public String[] selectImports(...) {
//这个方法返回的类全限定名的数据, spring会根据这个数组注入bean
...
}
//实现DeferredImportSelector方法
@Override
public Class<? extends Group> getImportGroup() {
return AutoConfigurationGroup.class;
}
//内部静态类 主要实现DeferredImportSelector.Group类
private static class AutoConfigurationGroup ...{
@Override
public void process(... {
...
//读取配置文件
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(...);
...
}
@Override
public Iterable<Entry> selectImports() {
//排除掉不符合条件配置类, 并排序
}
}
protected AutoConfigurationEntry getAutoConfigurationEntry(...) {
...
// 就是在这里参考SPI机制, 读取到所有的配置类信息
List<String> configurations = getCandidateConfigurations(...);
...
// 根据@Conditional进行过滤
configurations = filter(configurations, autoConfigurationMetadata);
...
return new AutoConfigurationEntry(configurations, exclusions);
}
}
一直追踪代码, 在SpringFactoriesLoader中看到最终读取了META-INF/spring.factories这个文件, 到这里就跟SPI长的很像了~
public final class SpringFactoriesLoader {
public static List<String> loadFactoryNames(...) {
String factoryClassName = factoryClass.getName();
//读取所有配置文件(类)
Map<String, List<String>> map = loadSpringFactories(classLoader)
//以EnableAutoConfiguration.class为key从map中获取自动配置类
return map .getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(...) {
//先走缓存, 说明之前读过, 就是在SpringApplication.run的时候, new SpringApplication过程中加载初始化器的时候, 有兴趣可以去看看
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
//在这里读了META-INF/spring.factories
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources("META-INF/spring.factories") :
ClassLoader.getSystemResources("META-INF/spring.factories"));
}
}
2、自动配制
Spring Boot启动, 先创建SpringApplication然后执行run方法
public static ConfigurableApplicationContext run(...) {
return new SpringApplication(primarySources).run(args);
}
2.1、创建SpringApplication
可以看到这个时候就已经读取了/META-INF/spring.factories文件, 并把它放到缓存中
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
// 记录主启动类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 设置初始化器(从/META-INF/spring.factories文件中读取)
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置监听器(从/META-INF/spring.factories文件中读取)
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 记录启动类
this.mainApplicationClass = deduceMainApplicationClass();
}
2.2、执行run方法过程中, 最主要有2个方法prepareContext和refreshContext
prepareContext()
主要完成上下文对象初始化操作, 其中最重要的就是load方法, 在这个方法中将启动类封装成Beandefinition注册到registry中, 方便后面进行BeanFactoryPostProcess执行的时候, 完成@SpringBooApplicaion,@EnableAutoConfiguration等注解的解析。
private void prepareContext(...) {
...
// 拿到启动类
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 将启动类封装成beanDefinition注册到到registry中 annotatedReader.register(source)
load(context, sources.toArray(new Object[0]));
...
}
refreshContext()
主要完成容器的刷新过程, 会调用spring的refresh()方法完成应用程序的启动, 这个过程中会调用 invokeBeanFactoryPostProcessors() 方法。
主要是在ConfigurationClassPostProcessor类中进行处理。
这个类是BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor的实现类。
在执行BeanDefinitionRegistryPostProcessor增强方法时会解析启动类上的所有注解, 比如:@ComponmentScans,@PropertySource,@Import等, 最主要就是**@Import**注解。
public static void invokeBeanFactoryPostProcessors(...) {
// 执行BeanDefinitionRegistryPostProcessor增强方法
// 主要就是在ConfigurationClassPostProcessor 类中实现的
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
}
public class ConfigurationClassPostProcessor implements ...{
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
...
processConfigBeanDefinitions(registry);
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
...
do {
//在这里进行了解析, candidates中就是启动类
parser.parse(candidates);
...
}while (!candidates.isEmpty());
...
}
public void parse(Set<BeanDefinitionHolder> configCandidates) {
...
//在这个方法中执行doProcessConfigurationClass
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
//在这里就跟执行了DeferredImportSelector.Group.process()
this.deferredImportSelectorHandler.process();
}
protected final SourceClass doProcessConfigurationClass(...) throws IOException {
...
// 在这里递归解析了 @Import 注解
processImports(configClass, sourceClass, getImports(sourceClass), true);
}
}
在解析@Import
注解的时候, 从主类开始一直递归解析出所有@Import注解, 此时将@EnableAutoConfiguration中 @Import(AutoConfigurationImportSelect.class
)解析出来, 后续在process()方法中调用AutoConfigurationImportSelect类支持的方法(process和selectImports),完成所有META-INF/spring.fatories文件中自动配置类的读取。并将所有符合条件的配置类封装成beanDefinition注册到registry
public class ConfigurationClassPostProcessor implements ...{
public void parse(Set<BeanDefinitionHolder> configCandidates) {
...
//在这里就跟执行了DeferredImportSelector.Group.process()
this.deferredImportSelectorHandler.process();
}
public void process() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
//遍历所有Import
grouping.getImports().forEach(entry -> {
...
}
});
}
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
//执行了AutoConfigurationImportSelector.AutoConfigurationGroup.process()
方法
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
}
private static class AutoConfigurationImportSelector.AutoConfigurationGroup ... {
@Override
public void process(...) {
...
//这个方法熟悉把度过文章开有那篇文章应该这知道这里边干了啥
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(getAutoConfigurationMetadata(),annotationMetadata);
...
}