参考:
先看springboot2.0自动注入文件spring.factories如何加载详解 AutoConfigurationImportSelector.java:
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
调用了getCandidateConfigurations:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
然后又调用了
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
我们看到
loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
这句话。loadSpringFactories(classLoader)
的返回值是Map<String, List<String>>
,它分析所有包下的META-INF/spring.factories
,将其中配置的k-v对合并。
getOrDefault(factoryClassName, Collections.emptyList());
中,factoryClassName
的值是org.springframework.boot.autoconfigure.EnableAutoConfiguration
(参考文章已经分析过为什么)。
所以合并起来,这句话的意思就是,读取所有包下的META-INF/spring.factories
,将其中配置的k-v对合并,再读取key为org.springframework.boot.autoconfigure.EnableAutoConfiguration
的value数组,将这个数组返回。这也正是loadFactoryNames
所做的事情了。
之后理解getAutoConfigurationEntry
这个方法:
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
大概就是configurations = removeDuplicates(configurations);
去除重复的配置类,configurations.removeAll(exclusions);
去除要除外的配置类,最后封装一下,返回结果。
我们再看到外层的AutoConfigurationImportSelector.selectImports:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
中最后一句话:
StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
通过autoConfigurationEntry.getConfigurations()
获取要配置的类,再转换成String数组,包含要导入的配置。之后由框架加载。
阅读Springboot - @Import 详解 的 Springboot 对@Import注解的处理过程 和Spring 工具类 ConfigurationClassParser 分析得到配置类在ConfigurationClassParser.parse中处理配置类
springboot处理@Import的分析:
this.deferredImportSelectorHandler.process();
,处理DeferredImportSelector的实现类selectImports
,对于DeferredImportSelector会先加入List中,在this.deferredImportSelectorHandler.process();
中回调其process
方法。配博客原文的一张图帮助理解:
另外,由于spring版本问题,图中的processDeferredImportSelector
应改为this.deferredImportSelectorHandler.process();
。
剩下的看引入的博客就好。
了解了原理,我们看到mybatis-autoconfigure包下的spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
其中,MybatisAutoConfiguration是一个@Configuration,定义了一些默认的Bean。 所以,该文件通过让MybatisAutoConfiguration自动加载,引入了一些默认的Bean,比如SqlSessionFactory、SqlSessionTemplate等。
spring-boot-configuration-processor的作用认为是用来引入@PropertySource的。 也有别的博客说是用来使@ConfigurationProperties生效的。 但我发现不导入spring-boot-configuration-processor也能使用这两个注解。所以我也搞不懂spring-boot-configuration-processor是做什么的。
SpringBoot标签之@ConfigurationProperties、@PropertySource注解的使用
这个文件是如何被加载的:
META-INF/spring-autoconfigure-metadata.properties
,以key-value对的形式存储在AutoConfigurationMetadata中。configurations = filter(configurations, autoConfigurationMetadata);
用到了上一步传入的参数filter.match(candidates, autoConfigurationMetadata);
,看到match的接口注释,就知道该方法返回一个bool数组,代表candidates中哪些是Configuration是满足加载条件的。
至于match又是怎么做的,再往深我就没探究了。这个文件毕竟是spring-boot-autoconfigure-processor自动生成的,用于spring加快加载速度用的,我们只要会用就好,不必过于关注其原理。
ImportSelector DeferredImportSelector区别 DeferredImportSelector会在其它ImportSelector加载完成后才加载。 DeferredImportSelector的载入:
// ConfigurationClassParser.processImports
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(
configClass, (DeferredImportSelector) selector);
}
DeferredImportSelector的回调:
ConfigurationClassParser.parse->this.deferredImportSelectorHandler.process();
->handler.processGroupImports();
。
其中,
process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector)
和selectImports()
@Configuration
@ConditionalOnClass({Billy.class})
public class VanConfig {
// ...
}
作用在@Configuration上,当Billy.class存在于classpath时,才加载VanConfig。 conditional系列