加载所有的自动配置类,@EnableAutoConfiguration 注解生效时机
上一篇文章我们跟踪代码看到了加载启动类目录下的bean的定义信息的过程,今天我们先分析下系统内置的自动配置类到底是啥时候加载进来的,因为tomcat等也是通过自动配置类先创建的工厂,后面才能讨论tomcat等容器的启动
接上文this.doProcessConfigurationClass(configClass, sourceClass);方法(ConfigurationClassParser类中的方法)
在使用componentScanParser扫码完入口类所在的包路径后,
进行调用
this.processImports(configClass, sourceClass, this.getImports(sourceClass),方法
1、this.getImports(sourceClass)
先进入this.getImports方法
private Set<ConfigurationClassParser.SourceClass> getImports(ConfigurationClassParser.SourceClass sourceClass) throws IOException {
Set<ConfigurationClassParser.SourceClass> imports = new LinkedHashSet();
Set<ConfigurationClassParser.SourceClass> visited = new LinkedHashSet();
this.collectImports(sourceClass, imports, visited);
return imports;
}
关注this.collectImports(sourceClass, imports, visited);方法,此方法是个自循环方法,非常绕
private void collectImports(ConfigurationClassParser.SourceClass sourceClass, Set<ConfigurationClassParser.SourceClass> imports, Set<ConfigurationClassParser.SourceClass> visited) throws IOException {
if (visited.add(sourceClass)) {
//获取当前类的所有注解信息
Iterator var4 = sourceClass.getAnnotations().iterator();
while(var4.hasNext()) {
ConfigurationClassParser.SourceClass annotation = (ConfigurationClassParser.SourceClass)var4.next();
//获取每个注解的名字
String annName = annotation.getMetadata().getClassName();
//如果注解不是以java开头则继续循环调用注解的注解
if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
this.collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
上面代码中可以看到不是java开头的注解会被处理,下面看下@SpringBootConfiguration注解信息
看下最后一行代码
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), “value”));最终会把符合条件的放在imports集合中(set集合),注意放入时取得是@Import注解属性得value值,最终为@EnableAutoConfiguration和@Registrar
小结:
getImport最终返回数据
2、this.processImports(configClass, sourceClass, this.getImports(sourceClass)
private void processImports(ConfigurationClass configClass, ConfigurationClassParser.SourceClass currentSourceClass, Collection<ConfigurationClassParser.SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {
if (!importCandidates.isEmpty()) {
if (checkForCircularImports && this.isChainedImportOnStack(configClass)) {
this.problemReporter.error(new ConfigurationClassParser.CircularImportProblem(configClass, this.importStack));
} else {
this.importStack.push(configClass);
try {
//遍历之前获取得sourceClass(关注 EnableAutoConfigurationImportSelector)
Iterator var5 = importCandidates.iterator();
while(true) {
while(true) {
while(var5.hasNext()) {
ConfigurationClassParser.SourceClass candidate = (ConfigurationClassParser.SourceClass)var5.next();
Class candidateClass;
if (candidate.isAssignable(ImportSelector.class)) {
//获取sourceClass得Class信息
candidateClass = candidate.loadClass();
//创建对象(EnableAutoConfigurationImportSelector被创建)
ImportSelector selector = (ImportSelector)BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
//判断是否要延迟加载selectImports方法
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
//EnableAutoConfigurationImportSelector走这里
//创建对象封装selector到ConfigurationClassParser内部属性中,见下图
this.deferredImportSelectors.add(new ConfigurationClassParser.DeferredImportSelectorHolder(configClass, (DeferredImportSelector)selector));
} else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<ConfigurationClassParser.SourceClass> importSourceClasses = this.asSourceClasses(importClassNames);
this.processImports(configClass, currentSourceClass, importSourceClasses, false);
}
} else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar = (ImportBeanDefinitionRegistrar)BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
} else {
this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
this.processConfigurationClass(candidate.asConfigClass(configClass));
}
}
return;
}
}
} catch (BeanDefinitionStoreException var15) {
throw var15;
} catch (Throwable var16) {
throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", var16);
} finally {
this.importStack.pop();
}
}
}
}
3、processDeferredImportSelectors方法调用
处理延迟加载selectImports方法得,该方法在parse方法内部
代码好短,但是又调用了这个processImports方法
private void processDeferredImportSelectors() {
//本地属性赋值此时为封装了EnableAutoConfigurationImportSelector得集合
List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
Iterator var2 = deferredImports.iterator();
while(var2.hasNext()) {
ConfigurationClassParser.DeferredImportSelectorHolder deferredImport = (ConfigurationClassParser.DeferredImportSelectorHolder)var2.next();
ConfigurationClass configClass = deferredImport.getConfigurationClass();
//重点001
String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
this.processImports(configClass, this.asSourceClass(configClass), this.asSourceClasses(imports), false);
}
}
- 重点001
3.1、继续调用了EnableAutoConfigurationImportSelector父类AutoConfigurationImportSelector得selectImports方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
try {
//关注点1
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//关注点2
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
configurations = this.sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//关注点3
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return (String[])configurations.toArray(new String[configurations.size()]);
} catch (IOException var6) {
throw new IllegalStateException(var6);
}
}
}
- 关注点1
上图可以看到,加载路径META-INF/spring-autoconfigure-metadata.properties
继续深入就是把资源使用properties进行加载
看下这个文件中是啥 - 关注点2
主要是用properties加载META-INF/spring.factories文件里面的数据
我们看下这个文件
- 关注点3
这个是进行过滤的,就是把不满足的自动配置先去掉
过滤前我们看下,大概有99个自动配置类,进入过滤方法
获取过滤器主要是从spring.factories文件获取的
过滤方法比较简单,但是比较绕
进入this.getOutcomes方法
分段进行处理的我们本文只看一个OnClassCondition.OutcomesResolver secondHalfResolver对象的创建
secondHalfResolver.resolveOutcomes()方法调用
以RedisRepositoriesAutoConfiguration为例
最后看下加载器怎么判断是否环境中有某个Class信息
过滤器结束后当前满足条件的只有如下23个
回到processDeferredImportSelectors方法
processImports方法会重复对这些自动配置类进行处理,加载bean的定义信息等,该方法的分析在和最上面一样,我们要注意到,该方法后面这行代码
this.processConfigurationClass(candidate.asConfigClass(configClass));
这个会将每次处理的Class放入this.configurationClasses.put(configClass, configClass); 中,是ConfigurationClassParser类的属性