Spring Boot @EnableAutoConfiguration和
@Configuration的區別
在Spring Boot中,我們會使用@SpringBootApplication來開啟Spring Boot程序。在之前的文章中我們講到了@SpringBootApplication相當於@EnableAutoConfiguration,@ComponentScan,@Configuration三者的集合。
其中@Configuration用在類上面,表明這個是個配置類,如下所示:
@Configuration
public class MySQLAutoconfiguration {
...
}
而@EnableAutoConfiguration則是開啟Spring Boot的自動配置功能。什么是自動配置功能呢?簡單點說就是Spring Boot根據依賴中的jar包,自動選擇實例化某些配置。
接下來我們看一下@EnableAutoConfiguration是怎么工作的。
先看一下@EnableAutoConfiguration的定義:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/** * Exclude specific auto-configuration classes such that they will never be applied. * @return the classes to exclude */
Class<?>[] exclude() default {};
/** * Exclude specific auto-configuration class names such that they will never be * applied. * @return the class names to exclude * @since 1.3.0 */
String[] excludeName() default {};
}
注意這一行: @Import(AutoConfigurationImportSelector.class)
AutoConfigurationImportSelector實現了ImportSelector接口,並會在實例化時調用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());
}
這個方法中的getCandidateConfigurations會從類加載器中查找所有的META-INF/spring.factories,並加載其中實現了@EnableAutoConfiguration的類。 有興趣的朋友可以具體研究一下這個方法的實現。
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
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 factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
我們再看一下spring-boot-autoconfigure-2.2.2.RELEASE.jar中的META-INF/spring.factories。
spring.factories里面的內容是key=value形式的,我們重點關注一下EnableAutoConfiguration:
# 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,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
...
這里只列出了一部分內容,根據上面的代碼, 所有的EnableAutoConfiguration的實現都會被自動加載。這就是自動加載的原理了。
如果我們仔細去看具體的實現:
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(JmxAutoConfiguration.class)
@ConditionalOnProperty(prefix = "spring.application.admin", value = "enabled", havingValue = "true",
matchIfMissing = false)
public class SpringApplicationAdminJmxAutoConfiguration {
可以看到里面使用了很多@Conditional*** 的注解,這種注解的作用就是判斷該配置類在什么時候能夠起作用。
更多教程請參考 flydean的博客