在SpringBoot中,EnableAutoConfiguration注解用于开启自动装配功能。

本文将详细分析该注解的工作流程。

EnableAutoConfiguration注解

启用SpringBoot自动装配功能,尝试猜测和配置可能需要的组件Bean。

自动装配类通常是根据类路径和定义的Bean来应用的。例如,如果类路径上有tomcat-embedded.jar,那么可能需要一个TomcatServletWebServerFactory(除非已经定义了自己的Servlet WebServerFactory Bean)。

自动装配试图尽可能地智能化,并将随着开发者定义自己的配置而取消自动装配相冲突的配置。开发者可以使用exclude()排除不想使用的配置,也可以通过spring.autoconfig.exclude属性排除这些配置。自动装配总是在用户定义的Bean注册之后应用。

用@EnableAutoConfiguration注解标注的类所在包具有特定的意义,通常用作默认扫描的包。通常建议将@EnableAutoConfiguration(如果没有使用@SpringBootApplication注解)放在根包中,以便可以搜索所有子包和类。

自动装配类是普通的Spring @Configuration类,使用SpringFactoriesLoader机制定位。通常使用@Conditional方式装配,最常用的是@ConditionalOnClass和@ConditionalOnMissingBean注解。

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration { /**
* Exclude specific auto-configuration classes such that they will never be applied.
*/
Class<?>[] exclude() default {}; /**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* 当类路径下没有指定的类时,可以使用这个属性指定排除的类
*/
String[] excludeName() default {};
}

该注解Import了AutoConfigurationImportSelector类,AutoConfigurationImportSelector类实现了DeferredImportSelector接口。

Import注解和DeferredImportSelector接口在之前的"Spring @Import注解源码分析"中详细分析过,此处在介绍它们,只分析AutoConfigurationImportSelector的工作流程。

AutoConfigurationImportSelector类

DeferredImportSelector接口

A variation of ImportSelector that runs after all @Configuration beans have been processed. This type of selector can be particularly useful when the selected imports are @Conditional.

Implementations can also extend the org.springframework.core.Ordered interface or use the org.springframework.core.annotation.Order annotation to indicate a precedence against other DeferredImportSelectors.

Implementations may also provide an import group which can provide additional sorting and filtering logic across different selectors.

AutoConfigurationGroup类

AutoConfigurationImportSelector的getImportGroup方法返回了AutoConfigurationGroup类。

private static class AutoConfigurationGroup implements
DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware { private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>(); private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>(); // ... 略 @Override
public void process(
AnnotationMetadata annotationMetadata,
DeferredImportSelector deferredImportSelector) { // AutoConfigurationEntry类使用List保存Configuration类
AutoConfigurationEntry autoConfigurationEntry =
((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
} @Override
public Iterable<Entry> selectImports() {
// 查找排除的配置类
Set<String> allExclusions = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getExclusions)
.flatMap(Collection::stream)
.collect(Collectors.toSet());
// 所有配置类
Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations)
.flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
// 将排除的配置类移除掉
processedConfigurations.removeAll(allExclusions); // 排序
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
} // ... 略
}

从上面的代码可以看出,查找自动装配类的逻辑在getAutoConfigurationEntry方法中。

getAutoConfigurationEntry方法

从META-INF/spring.factories文件解析EnableAutoConfiguration配置。

META-INF/spring.factories文件示例:

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 查找自动装配类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 以下几行为查找排除类、过滤等操作
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 这里的Filter是从META-INF/spring.factories文件解析出来的
configurations = getConfigurationClassFilter().filter(configurations);
// 触发事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
} protected List<String> getCandidateConfigurations(
AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 从META-INF/spring.factories文件查找EnableAutoConfiguration配置
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
return configurations;
}

SpringFactoriesLoader类loadFactoryNames方法

Load the fully qualified class names of factory implementations of the given type from "META-INF/spring.factories", using the given class loader.

public static List<String> loadFactoryNames(Class<?> factoryType, ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
} private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
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<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 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);
}
}

springboot启动流程 (3) 自动装配的更多相关文章

  1. SpringBoot启动流程与自动装配

    是什么 Spring Boot基于Spring框架之上的一个微服务架构开发框架大大简化了Spring的开发.因为Spring Boot提供了大量的自动配置.而且它是基于Java配置方式的开发(全注解) ...

  2. SpringBoot启动代码和自动装配源码分析

    ​ 随着互联网的快速发展,各种组件层出不穷,需要框架集成的组件越来越多.每一种组件与Spring容器整合需要实现相关代码.SpringMVC框架配置由于太过于繁琐和依赖XML文件:为了方便快速集成第三 ...

  3. SpringBoot启动流程分析(五):SpringBoot自动装配原理实现

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  4. SpringBoot启动流程解析

    写在前面: 由于该系统是底层系统,以微服务形式对外暴露dubbo服务,所以本流程中SpringBoot不基于jetty或者tomcat等容器启动方式发布服务,而是以执行程序方式启动来发布(参考下图ke ...

  5. SpringBoot启动流程分析(六):IoC容器依赖注入

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  6. SpringBoot启动流程分析(一):SpringApplication类初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  7. SpringBoot启动流程分析(二):SpringApplication的run方法

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  8. SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  9. SpringBoot启动流程分析(四):IoC容器的初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  10. SpringBoot源码学习3——SpringBoot启动流程

    系列文章目录和关于我 一丶前言 在 <SpringBoot源码学习1--SpringBoot自动装配源码解析+Spring如何处理配置类的>中我们学习了SpringBoot自动装配如何实现 ...

随机推荐

  1. 神经网络优化篇:详解正则化(Regularization)

    正则化 深度学习可能存在过拟合问题--高方差,有两个解决方法,一个是正则化,另一个是准备更多的数据,这是非常可靠的方法,但可能无法时时刻刻准备足够多的训练数据或者获取更多数据的成本很高,但正则化通常有 ...

  2. 华企盾DSC客户端连不上服务器(客户端在回收站)

    解决方法:确保该计算机在CLIENT表recyclebin列中的值为False,最好把所有计算机都改成False,改好后重启DSC服务器,最后重新安装客户端. 或者将客户端从回收站移动到正常的工作组.

  3. adb shell getprop 获取系统属性

    adb shell getprop 以华为p30为例: [gsm.default.apn]: [gsm.defaultpdpcontext.active]: true [gsm.dualcards.s ...

  4. Javascript Ajax总结——其他跨域技术之图像Ping和JSONP

    在CORS出现之前,为实现跨域Ajax通信,开发人员利用DOM中能够执行跨域请求的功能,在不依赖XHR对象的情况下也能发送某种请求.1.图像Ping这里使用<img>标签.一个网页可以从任 ...

  5. vulnhub - lazySysAdmin - writeup

    信息收集 可以看到目标开放了常见的22, 80, 139, 445, 3306这个6667的服务少见. root@kali tmp/lazySysAdmin » arp-scan -I eth1 -l ...

  6. dotnet-dump工具使用

    介绍 dotnet-dump 是 .NET Core 官方工具之一,用于生成和分析 .NET Core 进程的转储文件(dump file).它可以帮助开发人员在应用程序发生故障或性能问题时进行故障排 ...

  7. 开源、强大的Linux服务器集群管理工具,比宝塔好用!

    在这之前肯定很多人都接触过Linux管理面板:宝塔,宝塔的确非常方便而且好用,安装也简单,复制粘贴几句命令即可安装完成,且提供免费版.今天呢,民工哥向大家介绍另一个Linux的服务器管理面板--App ...

  8. Java播放MP3播放音频

    Java播放MP3播放音频 下面我演示用jdk自带包.框架等分别展示播放mp3.等music 一.使用javafx包 AudioClip 注意jdk11以上剥离了javafx public stati ...

  9. linux安装redis、ununtu和centos等类似的发行版安装redis,完全离线安装reids,自定义版本redis

    redis各个版本源码包下载:Index of /releases/ (redis.io) 下载与解压 从Index of /releases/ (redis.io)选择自己需要的版本并下载(此处以r ...

  10. 2024-01-13:用go语言,现在有一个打怪类型的游戏,这个游戏是这样的,你有n个技能, 每一个技能会有一个伤害, 同时若怪物小于等于一定的血量,则该技能可能造成双倍伤害, 每一个技能最多只能释放

    2024-01-13:用go语言,现在有一个打怪类型的游戏,这个游戏是这样的,你有n个技能, 每一个技能会有一个伤害, 同时若怪物小于等于一定的血量,则该技能可能造成双倍伤害, 每一个技能最多只能释放 ...