@EnableAutoConfiguration
自动装配注解的语义实现。
注解定义
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@AutoConfigurationPackage
// 导入实现AutoConfigurationImportSelector
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
// 可通过环境变量关闭自动配置
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
在Spring扫描加载类过程中会执行AutoConfigurationImportSelector
中的相关方法,AutoConfigurationImportSelector实现了Spring的DeferredImportSelector
就会在ConfigurationClassParse
类中得到解析并回调相关接口方法。
ConfigurationClassParser中解析ImportSelector
在Bean扫描过程中有解析DeferredImportSelector
实现的逻辑:
// ConfigurationClassParser
for (DeferredImportSelectorGrouping grouping : groupings.values()) {
// grouping.getImports()获取导入的配置类
grouping.getImports().forEach((entry) -> {
ConfigurationClass configurationClass = configurationClasses.get(
entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
// 执行DeferredImportSelector中的钩子方法
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
钩子方法AutoConfigurationGroup#process
实现
在AutoConfigurationImportSelector
中定义内部类AutoConfigurationGroup
private static class AutoConfigurationGroup implements DeferredImportSelector.Group,
BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
private ClassLoader beanClassLoader;
private BeanFactory beanFactory;
private ResourceLoader resourceLoader;
private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
// 被调用的钩子方法
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
// AutoConfigurationImportSelector#selectImports
// 还是调用了ImportSelector来处理获取需要加载自动装配的配置类
String[] imports = deferredImportSelector.selectImports(annotationMetadata);
for (String importClassName : imports) {
// 存起来,调用DeferredImportSelector.Group#selectImports时返回
this.entries.put(importClassName, annotationMetadata);
}
}
@Override
public Iterable<Entry> selectImports() {
// 返回排序后的导入配置类
return sortAutoConfigurations().stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName),
importClassName))
.collect(Collectors.toList());
}
private List<String> sortAutoConfigurations() {
// 排序
List<String> autoConfigurations = new ArrayList<>(this.entries.keySet());
if (this.entries.size() <= 1) {
return autoConfigurations;
}
// 超过1个时排序
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
//
return new AutoConfigurationSorter(getMetadataReaderFactory(),autoConfigurationMetadata)
.getInPriorityOrder(autoConfigurations);
}
private MetadataReaderFactory getMetadataReaderFactory() {
try {
return this.beanFactory.getBean(
SharedMetadataReaderFactoryContextInitializer.BEAN_NAME,
MetadataReaderFactory.class);
} catch (NoSuchBeanDefinitionException ex) {
return new CachingMetadataReaderFactory(this.resourceLoader);
}
}
}
在方法中最终还是调用了deferredImportSelector.selectImports(annotationMetadata);
进一步获取需要自动装配的配置类。我们继续看AutoConfigurationImportSelector
AutoConfigurationImportSelector
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
private static final String[] NO_IMPORTS = {};
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
// 容器上下文相关
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ClassLoader beanClassLoader;
private ResourceLoader resourceLoader;
}
ImportSelector#selectImports()
的实现
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 这里用AutoConfigurationMetadataLoader
// 加载所有jar包中的 META-INF/spring-autoconfigure-metadata.properties 文件
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
//
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 加载所有jar包中的spring.factories
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重
configurations = removeDuplicates(configurations);
// 排除注解指定排除的类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 过滤
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回最终需要自动装配的配置类
return StringUtils.toStringArray(configurations);
}
这里主要完成SPI配置文件的加载解析:
spring-autoconfigure-metadata.properties
的加载及解析spring.factories
文件的加载及解析,参见- 根据配置规则进行解析过滤,得到最终的自动装载配置类
spring-autoconfigure-metadata.properties
文件的加载及作用
final class AutoConfigurationMetadataLoader {
protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, PATH);
}
// 加载所有jar包中的指定文件
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
try {
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(path) : ClassLoader.getSystemResources(path));
Properties properties = new Properties();
while (urls.hasMoreElements()) {
// 放入容器中
properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
}
// 包装一下
return loadMetadata(properties);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
}
}
}
看看spring-autoconfigure-metadata.properties
的配置内容格式
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration=
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration.Configuration=
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,org.springframework.data.cassandra.core.ReactiveCassandraTemplate,reactor.core.publisher.Flux
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration.ConditionalOnClass=org.apache.solr.client.solrj.SolrClient,org.springframework.data.solr.repository.SolrRepository
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration=
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration=
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration.Configuration=
例如:org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration.ConditionalOnClass = org.apache.solr.client.solrj.SolrClient,org.springframework.data.solr.repository.SolrRepository
这条配置表SolrRepositoriesAutoConfiguration
这个类的加载需要ClassPath下存在SolrRepository
这个类。
再然后就是加载spring.factories
这个配置文件中的内容,getCandidateConfigurations(annotationMetadata, attributes);
这里定义了全部可能被加载的配置类,参见。
接着对加载进来的配置类进行过滤
configurations = filter(configurations, autoConfigurationMetadata);
private List<String> filter(List<String> configurations,
AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
// SPI 加载过滤器
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
// 属性设置
invokeAwareMethods(filter);
// 匹配方法调用
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
skip[i] = true;
skipped = true;
}
}
}
if (!skipped) {
return configurations;
}
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
if (!skip[i]) {
result.add(candidates[i]);
}
}
// 返回符合条件的
return new ArrayList<>(result);
}
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
// 加载配置的条件评估工具
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class,
this.beanClassLoader);
}
这里的filter过滤条件是根据spring-autoconfigure-metadata.properties
这个配置文件中的条件过滤的,在Spring扫描BeanDefinition时定义了@Conditional
强大的条件功能配置时还提前搞这么复杂的配置过滤方式就是为了提高过滤速度,减小启动时间。AutoConfigurationImportFilter
接口的实现在SpringBoot中有唯一的实现OnClassCondition
,有兴趣时可以去具体学习分析。