共计 4631 个字符,预计需要花费 12 分钟才能阅读完成。
springboot 与 spring 相比,有一个劣势就是大部分配置都有一套默认设定,不用再写大量的配置文件,即“约定优于配置 ”。
而这是靠主动拆卸实现的。
一、主动加载入口
springboot 的启动形式如下:
@SpringBootApplication
public class SpringRunner {public static void main(String[] args) {SpringApplication.run(SpringRunner.class, args);
}
}
察看 @SpringBootApplication
注解
// ## 此注解非常重要
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {...}
// ## 此注解同样重要
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {...}
从下面的代码看出,最终会通过 @Import 注解进入 AutoConfigurationImportSelector
类(调用链:@SpringBootApplication->@EnableAutoConfiguration->@Import(AutoConfigurationImportSelector.class))
持续察看:
// == 1.AutoConfigurationImportSelector 实现 DeferredImportSelector 接
// 调用 getImportGroup()办法,返回 AutoConfigurationGroup 类
public class AutoConfigurationImportSelector implements DeferredImportSelector{
@Override
public Class<? extends Group> getImportGroup() {return AutoConfigurationGroup.class;}
}
// == 2. 实现 DeferredImportSelector.Group 接口
private static class AutoConfigurationGroup
implements DeferredImportSelector.Group{..}
}
Group 接口有两个办法:
// 加载配置信息
void process(AnnotationMetadata metadata, DeferredImportSelector selector);
// 包装成 Entry 对象并返回
Iterable<Entry> selectImports();
接下来别离看一下这两个办法的实现。
1、process()办法——配置文件加载
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
// == 配置加载
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
// org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取配置信息
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//...
}
// org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// ## spi 形式加载配置
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,
getBeanClassLoader());
return configurations;
}
之前介绍了 spi 配置加载,spi 形式会查找 META-INF 下的 spring.factories
文件
文件内容:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
// ... 省略...
依据 org.springframework.boot.autoconfigure.EnableAutoConfiguration
主动加载 Redis、Mongo 等 156 个默认配置
2、selectImports()办法——包装成 Entry
@Override
public Iterable<Entry> selectImports() {
// 配置排序,并包装成 Entry 返回
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
}
二、spring 初始化时加载 Entry 对象
Group 接口 selectImports()办法返回的 Entry 对象去了哪里?
其实 spring 初始化加载时会用到它们。
从 spring 的 refresh 办法(初始化外围办法)一路追踪,整个调用链如下:
AbstractApplicationContext # refresh
AbstractApplicationContext # invokeBeanFactoryPostProcessors
PostProcessorRegistrationDelegate # invokeBeanFactoryPostProcessors
PostProcessorRegistrationDelegate # invokeBeanDefinitionRegistryPostProcessors
ConfigurationClassPostProcessor # postProcessBeanDefinitionRegistry
ConfigurationClassPostProcessor # processConfigBeanDefinitions
ConfigurationClassParser # parse
ConfigurationClassParser$DeferredImportSelectorHandler # process
ConfigurationClassParser$DeferredImportSelectorGroupingHandler # processGroupImports
ConfigurationClassParser$DeferredImportSelectorGrouping # getImports
这些 Entry 最终会以 BeanDefinition 对象的模式,放入到 BeanDefinitionMap 中。
后续进入 spring bean 对象初始化、属性赋值流程了,不再细说。