Spring Boot自动装配原理(2)— Spring Cloud Alibaba微服务原理与实战笔记
上一节说到@Import注解导入的是一个AutoConfigurationImportSelector类,那么它一定会实现配置类的导入,那么这一节我们具体分析。
阅读时间预计:15min 😃
AutoConfigurationImportSelector
AutoConfigurationImportSelector实现了ImportSelector,而ImportSelector接口里面的selectImports方法返回一个String[]数组,这个数组可以指定需要装配到IoC容器的类,当在@Import中导入一个ImportSelector的实现类后,会把该实现类返回的Class名称都装在到IoC容器中。
public interface ImportSelector {
String[] selectImports(AnnotationMetadata var1);
@Nullable
default Predicate<String> getExclusionFilter() {
return null;
}
}
和@Configuration不同的是,ImportSelector可以实现批量装配,还可以通过逻辑处理来实现Bean的选择性装配,也就是时候可以根据上下文来决定哪些类能够被IoC容器初始化。
自动装配实现源码详解
首先,通过之前的章节可以猜想到,自动装配的核心是扫描约定目录下的文件进行解析,解析完成之后把得到的Configuration配置类通过ImportSelector进行导入,从而完成Bean的自动装配过程。
首先我们定位到AutoConfigurationImportSelector的selectImports方法,它是ImportSelector接口的实现,这个方法主要有两个功能:
-
AutoConfigurationMetadataLoader.loadMetadata从META-INF/spring-autoconfigure-metadata.properties中加载自动装配的条件元数据,简单来说就是只有满足条件的Bean才能进行装配。
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.cassandra.CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,reactor.core.publisher.Flux,org.springframework.data.cassandra.core.ReactiveCassandraTemplate org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration.ConditionalOnClass=org.apache.solr.client.solrj.SolrClient,org.springframework.data.solr.repository.SolrRepository org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration.ConditionalOnWebApplication=SERVLETa //省略部分
它是Bean装配前的条件配置,减少配置类的加载数量,加速启动时间。
-
收集所有符合条件的配置类autoConfigurationEntry.getConfigurations(),完成自动装配。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
我们重点分析一下配置类的收集方法getAutoConfigurationEntry,根据之前的猜想,这个方法实现的也应该是扫描指定路径下的文件解析得到需要装配的配置类,而这里用到了SpringFactoriesLoader。我们先分析这个收集方法:
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
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.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
它主要做以下几件事:
- getAttributes获得@EnableAutoConfiguration注解中的属性exclude、excludeName等。
- getCandidateConfigurations(获取候选配置)获得所有自动装配的配置类,后面重点分析。
- removeDuplicates去除重复的配置项。
- getExclusions根据@EnableAutoConfiguration注解中配置的exclude等属性,把不需要自动装配的配置类移除。
- fireAutoConfigurationImportEvents广播事件。
- 最后经过多层判断和过滤之后的配置类集合。
总的来说,它先获得所有的配置类,然后进行去重、exclude排除等操作,得到最终需要实现自动装配的配置类。这里需要重点关注的是getCandidateConfigurations,它是获得配置类最核心的方法:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
它用到了SpringFactoriesLoader,这是Spring内部提供的一种约定俗成的加载方式,类似于Java中的SPI。
简单来说:它会扫描classpath下的META-INF/spring.factories文件,里面的数据以key-value的方式存储,而SpringFactoriesLoader.loadFactoryNames会根据key得到value。因此,在这个场景中key就是EnableAutoConfiguration,value就是多个配置类,也就是getCandidateConfigurations返回的值。
# org.springframework.boot.autoconfigure 目录下的 META-INF/spring.factories
//省略部分
# 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,\
//省略部分
总结
至此,自动装配的原理基本就分析完成了,简单来总结一下核心过程:
- 通过@Import({AutoConfigurationImportSelector.class})实现配置类的导入,但这不是Spring传统意义上的单个配置类装配。
- AutoConfigurationImportSelector实现了ImportSelector接口,重写了方法selectImports,它用于实现选择性批量配置类的装配。
- 通过Spring提供的SpringFactoriesLoader机制,扫描classpath路径下的META-INF/spring.factories,读取需要实现自动装配的配置类。
- 通过条件筛选的方式,把不符合条件的配置类移除,最终完成自动装配。
下次我们手写一个自己的Starter~