@EnableAutoConfiguration 原理分析
@SpringBootApplication中包含了@EnableAutoConfiguration注解,@EnableAutoConfiguration的作用是启用Spring的自动加载配置。
SpringBoot一个最核心的观点就是,约定大于配置,这种看似降低了灵活度的方法,却大大简化了SpringBoot的开发过程。这种约定在实现角度看就是SpringBoot提供了大量的默认配置参数,那么问题来了,SpringBoot在哪里存放这些默认的配置参数,并如何自动配置这些默认的参数呢?
实际上,SpringBoot的很多Starter都有@Enable开头的注解,实现原理也很是类似,这里先来看这个注解的实现原理。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
这里我们可以看到@EnableAutoConfiguration使用@Import添加了一个AutoConfigurationImportSelector类,Spring自动注入配置的核心功能就依赖于这个对象。
在这个类中,提供了一个getCandidateConfigurations()
方法用来加载配置文件。借助Spring提供的工具类SpringFactories的loadFactoryNames()方法加载配置文件。扫描的默认路径位于META-INF/spring.factories
中。
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.Conditioneva luationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.Conditioneva luationReportAutoConfigurationImportListener
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
...
上边只是一部分的配置信息。等号左边的是对应配置的接口,而右边是配置类。我们来看具体是如何加载这些类的。
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// 从META-INF/spring.factories中加载urls。
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 从文件中加载配置
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
// 添加到缓存中防止重新加载
cache.put(classLoader, result);