目录
- 自动配置 @EnableAutoConfiguration
- 开启自动配置
- 读取配置
- 提前过滤
- 自动配置配置包 @AutoConfigurationPackage
自动配置 @EnableAutoConfiguration
开启自动配置
在Spring 启动类上的 @SpringBootApplication 中有 @EnableAutoConfiguration
读取配置
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
也就是导入了 AutoConfigurationImportSelector
我们在 Spring 时代知道这个类会返回配置类的全类名
也就是在 selectImports 方法中返回了自动配置的相关类的全类名的集合
Spring boot 自动装配的配置类有144个
如果一个一个写,那代码就太长了
所以Spring 就把这些配置类都配置在一个文件中
但是如果你debug到 selectImports() 里会发现并没有进这个方法
是因为 DeferredImportSelector 这个接口 他其实是进入的 getImportGroup()
public Class<? extends Group> getImportGroup() {return AutoConfigurationGroup.class;
}最终调用的是 org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process//最终还是会调用 deferredImportSelector
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).deferredImportSelector(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {this.entries.putIfAbsent(importClassName, annotationMetadata);
} //后续排序后返回
//排序规则:
1. 按 @AutoConfigureOrder 配置的顺序
2. 按 @AutoConfigureAfter 和 @AutoConfigureBefore排
getAutoConfigurationEntry(annotationMetadata)://读取 @EnableAutoConfiguration 的配置信息
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//读取所有的 META-INF/spring.factories 文件中配置的配置类
//读取所有的 @AutoConfiguration 注解的配置类
//这里会把 classLoader里所有的META-INF/spring.factories文件都读出来汇总
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//有序去重 这一步很好玩 平时也可以用
//new ArrayList<>(new LinkedHashSet<>(list));
configurations = removeDuplicates(configurations);//读取 注解中配置的排除类 排除掉
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);//这一步 是提前过滤一些肯定不能行的配置类
//具体怎么做的下边分析
configurations = getConfigurationClassFilter().filter(configurations);
//触发自动配置导入事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
META-INF/spring.factories 文件示例:
//文件内容 接口 = 类名列表,多个用“,”隔开文件解析方法 loadSpringFactories()://读取classLoader中所有的文件
Map<String, List<String>> result = new HashMap<>();
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {URL url = urls.nextElement();//读取并创建资源UrlResource resource = new UrlResource(url);//解析成 properties 其实就是个 MapProperties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryTypeName = ((String) entry.getKey()).trim();// 按 ‘,’ 分割String[] factoryImplementationNames =StringUtils.commaDelimitedListToStringArray((String) entry.getValue());for (String factoryImplementationName : factoryImplementationNames) {//这个就是常用的 map的value是listresult.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()).add(factoryImplementationName.trim());}}
}
return result;
提前过滤
getConfigurationClassFilter().filter(configurations):getConfigurationClassFilter():
//读取到三个
//OnBeanCondition
//OnClassCondition
//OnWebApplicationCondition
filters = SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
for (AutoConfigurationImportFilter filter : filters) {invokeAwareMethods(filter);
}
//这里再创建 ConfigurationClassFilter 的时候就读取了预处理的配置文件
//META-INF/spring-autoconfigure-metadata.properties
//这个配置类里边已经配好类名和提前过滤配置的内容
return this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);//根据配置类中的内容提前过滤一下
//OnBeanCondition :没有配置类型的Bean定义就别解析了
//OnClassCondition :没有配置的类文件就别解析了
//OnWebApplicationCondition :不是配置的上下文类型就别解析了
//这样一来就快速的过滤掉一些肯定不能创建的配置类
ConfigurationClassFilter.filter(configurations):
META-INF/spring-autoconfigure-metadata.properties 文件内容示例:
自动配置配置包 @AutoConfigurationPackage
这个注解是配置自动装配所作用的包的对象
在实际工作中有的时候需要把一个老项目代码迁移到新的项目中
为了避免出现大量改动可以把包平移到新的项目中
然后在新项目中配置扫描老项目的包名
有的时候会发现Bean注册没问题,但是一些自动装配扫描的Bean就没有注册
比如 ES 自动装配的时候就会基于这个 package 来扫描