由@EnableAutoConfiguration
可以看到,其主要功能通过@Import(AutoConfigurationImportSelector.class)
实现。
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
@Import
提供导入配置类的功能,可导入使用了@Configuration
注解的类或实现了ImportSelector
、ImportBeanDefinitionRegistrar
的类
public interface ImportSelector {
/**
* 提供了参数为AnnotationMetadata类型的方法,AnnotationMetadata包含@Import的注解信息
* 即selectImports方法根据@Import注解的value信息返回配置类的全类名,字符串数组形式
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
如下图所示,可以看到@EnableAutoConfiguration
中@Import(AutoConfigurationImportSelector.class)
,AutoConfigurationImportSelector
实现自DeferredImportSelector
接口,DeferredImportSelector
接口继承自ImportSelector
DeferredImportSelector
接口与ImportSelector
接口的不同:
DeferredImportSelector
:完成使用了@Configuration
等注解的类加载后,再加载selectImports
方法返回的指定的配置类ImportSelector
:完成使用了@Configuration
等注解的类加载前,加载selectImports
方法返回的指定的配置类DeferredImportSelector
源码可看出,DeferredImportSelector
提供了Group
接口public interface DeferredImportSelector extends ImportSelector {
/**
* Return a specific import group.
* The default implementations return {@code null} for no grouping required.
* @return the import group class, or {@code null} if none
* @since 5.0
*/
@Nullable
default Class<? extends Group> getImportGroup() {
return null;
}
/**
* Interface used to group results from different import selectors.
*/
interface Group {
/**
* Process the {@link AnnotationMetadata} of the importing @{@link Configuration}
* class using the specified {@link DeferredImportSelector}.
*/
void process(AnnotationMetadata metadata, DeferredImportSelector selector);
/**
* Return the {@link Entry entries} of which class(es) should be imported
* for this group.
*/
Iterable<Entry> selectImports();
}
查看Group
接口的实现类AutoConfigurationGroup
中的selectImport
,可追踪到类ConfigurationClassParser
中的parse
方法:
进入parse
方法,parse
中直接调用了processConfigurationClass
方法:
进入doProcessConfigurationClass方法:
可以看到,依次对使用了@Component、@PropertySource、@ComponentScan、@Import、@ImportResource、@Bean的类、接口中的默认方法、配置类的父类进行处理
进入处理@Import
的方法processImports:
根据处理ImportSelector
接口实现类的逻辑可以看出,如果实现了DeferredImportSelector
接口,则为延迟导入,通过deferredImportSelectorHandler
处理器进行处理,先加入集合,不会立即加载,这里即前文所述完成使用了@Configuration等注解的类加载后,再加载selectImports方法返回的指定的配置类,这里也不会去执行AutoConfigurationImportSelector
类重写的selectImports方法,而如果只实现了ImportSelector
接口,则递归解析,把解析到的类名按配置类直接完成加载,即在完成使用了@Configuration等注解的类加载前就进行了ImportSelector接口实现类的加载,从上面代码也可以看出,实际上只实现了ImportSelector
接口的类,会在@ImportResource
和@Bean
加载前完成加载
继续进入deferredImportSelectorHandler#handle
方法,生成了holder
对象,存到了deferredImportSelectors
中,这里只是将配置类存入了deferredImportSelectors
,还没有加载配置类
再来看下parse
方法中this.deferredImportSelectorHandler.process()
的处理逻辑:
进入register
方法和processGroupImports
方法:
进入getImports
,看到执行自定义的group#process
方法
process
方法中执行了自动装配
从上面分析过程也可以看出,自动配置时不会去执行AutoConfigurationImportSelector
类重写的selectImports
方法,而是通过自定义的group#process()
完成,本质上还是通过AutoConfigurationImportSelector#getAutoConfigurationEntry
获取待导入的bean,将自动配置类和被排除的类封装成了一个AutoConfigurationEntry
对象,与AutoConfigurationImportSelector#selectImports
类似
下面是AutoConfigurationImportSelector#getAutoConfigurationEntry
源码:
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
// 这步判断是否开启了自动配置,若未开启,返回空数组
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取注解的属性,此处返回key为 exclude和excludeName 的map
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 通过SpringFactoriesLoader.loadFactoryNames方法,获取spring.factories文件中所
// 有需自动配置的类全名,即 org.springframework.boot.autoconfigure.XXXX
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 通过new ArrayList<>(new LinkedHashSet<>(list))实现配置类去重,同时保证了顺序
configurations = removeDuplicates(configurations);
// 获取被排除的类集合,包括前面attributes中exclude和excludeName中的类,
// 和使用spring.autoconfigure.exclude指定的类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 通过ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)
// 检查被排除的类能否实例化及是否属于自动配置类,若可实例化且不是自动配置类,则抛出异常
checkExcludedClasses(configurations, exclusions);
// 删去被排除的类
configurations.removeAll(exclusions);
// 检查配置类的注解是否满足spring.factories文件中
// org.springframework.boot.autoconfigure.AutoConfigurationImportFilter
// 指定的注解检查条件(OnBeanCondition,OnClassCondition,
// OnWebApplicationCondition),筛选出满足的配置类
configurations = filter(configurations, autoConfigurationMetadata);
// 将配置类和被排除的类构建成AutoConfigurationImportEvent对象,传入监听器
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
处理流程总结如下:
下面对AutoConfigurationImportSelector#getAutoConfigurationEntry
进行详细介绍
protected boolean isEnabled(AnnotationMetadata metadata) {
// 这里会先判断是不是AutoConfigurationImportSelector类,若不是,默认返回true,即开启自动配置
// 若是AutoConfigurationImportSelector,则获取EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY属性,即
// spring.boot.enableautoconfiguration,获取不到也默认返回true
if (getClass() == AutoConfigurationImportSelector.class) {
return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
}
return true;
}
可以看到,无论是AutoConfigurationImportSelector#selectImports
中,还是自定义的AutoConfigurationGroup
中,加载元数据都是通过AutoConfigurationMetadataLoader.loadMetadata
完成
AutoConfigurationMetadataLoader#loadMetadata
:
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
try {
// 获取数据
Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
: ClassLoader.getSystemResources(path);
Properties properties = new Properties();
// 将URL的属性存入properties
while (urls.hasMoreElements()) {
properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
}
// 封装properties,返回AutoConfigurationMetadata的实现类PropertiesAutoConfigurationMetadata对象
return loadMetadata(properties);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
}
}
这里去加载META-INF/spring-autoconfigure-metadata.properties
文件,文件中配置如下:
自动配置类的全限定名.注解名=值
提前完成该数据的加载,可获取到所有配置类的各注解的值,也就拿到了@Conditional
注解的值,在获取自动配置类时,根据条件删去不符合的配置类,这一方式减少了配置类的数量,也就减少了初始化Bean的时间,有效缩短了SpringBoot的启动时间。
自动配置类的获取通过以下三行代码完成
AutoConfigurationImportSelector#getAttributes
获取注解的属性map
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
// getAnnotationClass()返回getAnnotationClass(),此处name即
// org.springframework.boot.autoconfigure.EnableAutoConfiguration
String name = getAnnotationClass().getName();
// 通过metadata.getAnnotationAttributes获取@EnableAutoConfiguration注解的属性,即exclude和excludeName
// 返回一个AnnotationAttributes对象,本质是一个key为exclude和excludeName,值为对应属性值的LinkedHashMap
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
// 此处@EnableAutoConfiguration注解的属性不然不会是空,是空则异常
Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
+ " annotated with " + ClassUtils.getShortName(name) + "?");
return attributes;
}
AutoConfigurationImportSelector#getCandidateConfigurations
获取自动配置类
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 如果configurations为空,会报错
// 参数getSpringFactoriesLoaderFactoryClass()返回值为EnableAutoConfiguration.class
// 此处只返回自动配置类
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
// 此处参数factoryType为EnableAutoConfiguration.class
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
// 全类名org.springframework.boot.autoconfigure.EnableAutoConfiguration
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// MultiValueMap的value可以是list
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// FACTORIES_RESOURCE_LOCATION=META-INF/spring.factories
// 即获取META-INF/spring.factories文件,存到Enumeration
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,实际是一个HashTable
// 此处key、value对应spring.factories文件内容,添加到result中
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
//这里的StringUtils.commaDelimitedListToStringArray将包含逗号的字符串按逗号分割后返回list
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
// 返回值result是个LinkedMultiValueMap
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
这是Map接口的getOrDefault方法,对前一步读取解析spring.factories得到的result进行处理,只获取key是org.springframework.boot.autoconfigure.EnableAutoConfiguration的配置类集合
default V getOrDefault(Object key, V defaultValue) {
// 如果map中包含该key或value不为空,返回对应value
// 否则返回默认值
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
AutoConfigurationImportSelector#removeDuplicates
方法对前面得到的配置类集合进行了去重处理
protected final <T> List<T> removeDuplicates(List<T> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}
可以看到,AutoConfigurationImportSelector#getCandidateConfigurations
最终返回的是所需自动配置类的Map,key为org.springframework.boot.autoconfigure.EnableAutoConfiguration
,值为自动配置类的list。
下面对通过@EnableAutoConfiguration
的注解属性exclude
或excludeName
、及配置文件指定spring.autoconfigure.exclude
排除不需要自动配置的类的实现原理进行介绍,即下面三行代码。
AutoConfigurationImportSelector#getExclusions
:首先获取需要排除的类
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
Set<String> excluded = new LinkedHashSet<>();
// @EnableAutoConfiguration注解的exclude属性的值集合
excluded.addAll(asList(attributes, "exclude"));
// @EnableAutoConfiguration注解的excludeName属性的值集合
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
// spring.autoconfigure.exclude配置项的值
excluded.addAll(getExcludeAutoConfigurationsProperty());
return excluded;
}
AutoConfigurationImportSelector#checkExcludedClasses
:检查被排除的类能否实例化及是否属于自动配置类,不满足任一点,异常
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
List<String> invalidExcludes = new ArrayList<>(exclusions.size());
// 遍历指定的需排除的类
for (String exclusion : exclusions) {
// ClassUtils.isPresent表示判断指定名称的类是否可实例化,此处即判断exclusion能否实例化
// 若exclusion可被实例化,且不属于自动配置类,则存入invalidExcludes
if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
invalidExcludes.add(exclusion);
}
}
// 若invalidExcludes不空,则进行异常处理
if (!invalidExcludes.isEmpty()) {
handleInvalidExcludes(invalidExcludes);
}
}
protected void handleInvalidExcludes(List<String> invalidExcludes) {
StringBuilder message = new StringBuilder();
for (String exclude : invalidExcludes) {
message.append("\t- ").append(exclude).append(String.format("%n"));
}
// 可以看到,抛出的异常为:以下类不是自动配置类,不能被排除
throw new IllegalStateException(String.format(
"The following classes could not be excluded because they are not auto-configuration classes:%n%s",
message));
}
得到被排除的类并完成检查后,通过removeAll
方法从configurations
集合中删去被排除的类
下面对AutoConfigurationImportSelector#filter
方法进行介绍:
// 参数configurations为经过去重、排除指定类后的spring.factories文件中的自动配置类
// 参数autoConfigurationMetadata为前面加载元数据步骤的结果,即从
// META-INF/spring-autoconfigure-metadata.properties文件加载到的配置
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
// configurations从list转出string数组
String[] candidates = StringUtils.toStringArray(configurations);
// 用于记录对应索引位置配置类是否需要满足spring-autoconfigure-metadata.properties文件中的配置条件
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
// getAutoConfigurationImportFilters方法见下文,此处按照filter依次进行处理
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
invokeAwareMethods(filter);
// 根据autoConfigurationMetadata中的条件配置处理candidates,true表示条件匹配成功
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
// 条件匹配失败,需要被排除
skip[i] = true;
// 该自动配置类置空
candidates[i] = null;
skipped = true;
}
}
}
// 全部匹配成功,直接返回configurations
if (!skipped) {
return configurations;
}
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
// skip[i]为false,条件匹配成功,加入自动配置类结果集
if (!skip[i]) {
result.add(candidates[i]);
}
}
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
}
return new ArrayList<>(result);
}
AutoConfigurationImportSelector#getAutoConfigurationImportFilters
通过SpringFactoriesLoader#loadFactories
方法获取spring.factories
文件中key为org.springframework.boot.autoconfigure.AutoConfigurationImportFilter
的属性值,即获取默认的filter类
# org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnBeanCondition,\ org.springframework.boot.autoconfigure.condition.OnClassCondition,\ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
FilteringSpringBootCondition#match
方法:
@Override
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
// 判断条件是否匹配核心方法,由FilteringSpringBootCondition类的三个子类分别实现
ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
// 下面将getOutcomes返回的结果转换成boolean数组
boolean[] match = new boolean[outcomes.length];
for (int i = 0; i < outcomes.length; i++) {
// outcomes[i]为空或isMatch()为true都表示条件匹配成功,match[i]取true,这点从下文的分析可以得出
match[i] = (outcomes[i] == null || outcomes[i].isMatch());
if (!match[i] && outcomes[i] != null) {
logOutcome(autoConfigurationClasses[i], outcomes[i]);
if (report != null) {
report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
}
}
}
return match;
}
getOutcomes
方法实现,以OnClassCondition
子类实现为例进行说明:
@Override
protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata) {
// Split the work and perform half in a background thread if more than one
// processor is available. Using a single additional thread seems to offer the
// best performance. More threads make things worse.
if (Runtime.getRuntime().availableProcessors() > 1) {
// 处理器为多个,采用后台线程处理,通过分半处理提升效率,本质还是
// 通过StandardOutcomesResolver#resolveOutcomes实现
return resolveOutcomesThreaded(autoConfigurationClasses, autoConfigurationMetadata);
}
else {
OutcomesResolver outcomesResolver = new StandardOutcomesResolver(autoConfigurationClasses, 0,
autoConfigurationClasses.length, autoConfigurationMetadata, getBeanClassLoader());
return outcomesResolver.resolveOutcomes();
}
}
OnClassCondition#resolveOutcomesThreaded
此处继续进入createOutcomesResolver
方法,可以看到new了一个ThreadedOutcomesResolver
对象
分半后进行处理时,先处理了普通的StandardOutcomesResolver
对象,然后处理ThreadedOutcomesResolver
对象,这两个类对resolveOutcomes
方法实现不同,普通的StandardOutcomesResolver
对象直接调用OnClassCondition#resolveOutcomes
,ThreadedOutcomesResolver
对象则通过this.thread.join();
使得主线程阻塞,等待ThreadedOutcomesResolver
对象中子线程处理结束后才继续主线程后续处理逻辑。
OnClassCondition#resolveOutcomes
OnClassCondition#getOutcomes
private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, int start, int end,
AutoConfigurationMetadata autoConfigurationMetadata) {
// 初始化outcomes存放结果
ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
for (int i = start; i < end; i++) {
// 获取当前处理的自动配置类,例如
// org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration
String autoConfigurationClass = autoConfigurationClasses[i];
if (autoConfigurationClass != null) {
// 执行AutoConfigurationMetadataLoader#get方法
// 获取元数据即spring-autoconfigure-metadata.properties中key为
// org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration.ConditionalOnClass
// 的属性值,即key为自动配置类名+ConditionalOnClass的属性值,也就是该配置类加载的条件
// 以此为例,条件value为org.springframework.jms.core.JmsTemplate
String candidates = autoConfigurationMetadata.get(autoConfigurationClass, "ConditionalOnClass");
// 存在加载条件,则进行判断,核心为getOutcome方法
if (candidates != null) {
outcomes[i - start] = getOutcome(candidates);
}
}
}
return outcomes;
}
private ConditionOutcome getOutcome(String className, ClassLoader classLoader) {
// ClassNameFilter.MISSING是FilteringSpringBootCondition的一个枚举类的属性,调用其内部方法matches
if (ClassNameFilter.MISSING.matches(className, classLoader)) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
.didNotFind("required class").items(Style.QUOTE, className));
}
return null;
}
以org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration
为例,返回的结果如下
static boolean isPresent(String className, ClassLoader classLoader) {
if (classLoader == null) {
classLoader = ClassUtils.getDefaultClassLoader();
}
try {
resolve(className, classLoader);
return true;
}
catch (Throwable ex) {
return false;
}
}
// 可以看到,即判断当前className能否当前类加载器加载,可以加载则匹配成功,否则匹配失败
protected static Class<?> resolve(String className, ClassLoader classLoader) throws ClassNotFoundException {
if (classLoader != null) {
return classLoader.loadClass(className);
}
return Class.forName(className);
}
到此,对过滤自动配置的分析就结束了,大致流程可总结如下:
AutoConfigurationImportSelector#fireAutoConfigurationImportEvents
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
// 通过SpringFactoriesLoader.loadFactories获取spring.factories中配置的Listener
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
// 将configurations和exclusions封装成event事件后,传入监听器
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
for (AutoConfigurationImportListener listener : listeners) {
invokeAwareMethods(listener);
listener.onAutoConfigurationImportEvent(event);
}
}
}
// 获取spring.factories中属性
// org.springframework.boot.autoconfigure.AutoConfigurationImportListener
// 的值
protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
}