上一小节发现,SpringBoot的启动类上有一个关键的组合注解SpringBootApplication,其中它包含的EnableAutoConfiguration注解实现了启动时的自动配置功能。
具体来说,是通过EnableAutoConfiguration注解里的
@Import(EnableAutoConfigurationImportSelector.class)和Spring自带的工具类SpringFactoriesLoader实现的自动配置。
下面详细探究一下自动配置的实现过程。
自动配置的源码追踪
EnableAutoConfigurationImportSelector类实现了DeferredImportSelector接口,DeferredImportSelector又继承了ImportSelector接口,因此EnableAutoConfigurationImportSelector类里需要对ImportSelector的selectImports()方法做具体的视实现:
EnableAutoConfigurationImportSelector类selectImports()的主要作用是:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
// 获取自动配置的Entry结构的数据
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
其中AutoConfigurationEntry的数据结构如下:
protected static class AutoConfigurationEntry {
private final List<String> configurations;
private final Set<String> exclusions;
private AutoConfigurationEntry() {
this.configurations = Collections.emptyList();
this.exclusions = Collections.emptySet();
}
...
}
进入this.getAutoConfigurationEntry(annotationMetadata):
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 获得AutoConfigurationEntry中的configurations数据
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
// 获得AutoConfigurationEntry中的exclusions数据
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}
先进入this.getCandidateConfigurations(annotationMetadata, attributes):
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
发现是通过SpringFactoriesLoader.loadFactoryNames()方法得到的configurations,而第一个入参传入的factoryType就是EnableAutoConfiguration.class:
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
// SpringFactoriesLoader工具类中
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
// factoryTypeName即为EnableAutoConfiguration
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
继续进入SpringFactoriesLoader工具类中的loadSpringFactories()方法:
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
Map<String, List<String>> result = new HashMap();
try {
// 加载了所有META-INF/spring.factories文件下的数据
Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Map.Entry<?, ?> entry = (Map.Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
String[] var10 = factoryImplementationNames;
int var11 = factoryImplementationNames.length;
for(int var12 = 0; var12 < var11; ++var12) {
String factoryImplementationName = var10[var12];
((List)result.computeIfAbsent(factoryTypeName, (key) -> {
return new ArrayList();
})).add(factoryImplementationName.trim());
}
}
}
result.replaceAll((factoryType, implementations) -> {
return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
});
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}
看到这里我们发现SpringFactoriesLoader#loadSpringFactories()方法就是把所有"META-INF/spring.factories"里的数据都加载进来,放到了一个Map<String, List<String>> result里面。
可能有人会有疑问,我的项目里没有spring.factories文件,springboot从哪读取的文件?
其实springboot的源码里有一个自动配置的库,这个库里的spring.factories文件已经存放了所有可支持自动配置的Configuration清单:
此时再回头看一下SpringFactoriesLoader#loadFactoryNames方法,最后一句return是:
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
还记得factoryTypeName参数是什么吗?就是EnableAutoConfiguration.class。
因此,return的List<String>里面存放的也就是Spring.factories文件里EnableAutoConfiguration所对应的value值,即各种各样的XxxAutoConfiguration。
我们再次回到getAutoConfigurationEntry方法:
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 这里我们拿到了各种各样的XxxAutoConfiguration
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回新建的AutoConfigurationEntry
return new AutoConfigurationEntry(configurations, exclusions);
}
}
然后再回到最初的起点:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
所以EnableAutoConfigurationImportSelector#selectImports方法返回的就是所有在spring.factories文件里key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的所有value值class路径,也就是所有被springboot的自动配置支持的Configuration类的路径。
所以,@EnableAutoConfiguration自动配置的原理是:从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射(Java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。
那么,EnableAutoConfigurationImportSelector#selectImports方法是在何时被调用的?它所返回的list又是如何被实例化为容器配置类并最终合并到Spring IoC容器的?还得接着往下看。
让Springboot自动配置自定义类
了解上面的自动配置加载过程以后,我们也可以利用这一过程让Springboot自动配置自定义的类:
只需要在项目的META-INF/spring.factories这样写:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.demo.service.MyOrder
这样我们就可以在spring容器里使用MyOrder的bean了。