在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. 国产 Web 组态软件在玻璃生产线中的应用

    ​  概述 随着工厂信息化.数字化发展,智慧生产车间成为必然发展趋势,通过智能硬件.物联网.大数据等智慧化技术与手段,提高车间生产设备.工艺设备的智能执行能力,从而提升整个车间乃至工厂的智能化.网络化 ...

  2. 【玩转腾讯混元大模型】怎么说?我用混元AI大模型开发了个IDEA插件

    前言 halo 我是杨不易呀,在混元大模型内测阶段就已经体验了一番当时打开页面的时候灵感模块让我大吃一惊这么多角色模型真的太屌了,随后我立马进行了代码处理水平和上下文的效果结果一般般但是到如今混元大模 ...

  3. 从零玩转设计模式之原型模式-yuanxingmoshi

    title: 从零玩转设计模式之原型模式 date: 2022-12-11 20:05:35.488 updated: 2022-12-23 15:35:44.159 url: https://www ...

  4. 部署堡垒机2——安装MySQL8.0.32或8.0.28+

    MySQL的三大版本 a)MySQL Enterprise Edition:企业版本(付费)b)MySQL Cluster CGE:高级集群版(收费)c)MySQL Community Server: ...

  5. Flume快速入门

    Flume快速入门 一.简介 高可用.高可靠,分布式的海量日志采集.聚合和传输系统,基于流式架构,灵活简单. event:事件 source:数据源 sink:目标 channel:数据管道 通过获取 ...

  6. Eclipse部署虚拟项目目录

    目录 1. 问题 2. 方案 3. 参考 1. 问题 对于一些附带了大量本地资源的项目(例如,用户上传的文件,地图切片或者三维模型等),在Eclipse中部署调试是我一直头痛的问题.因为Eclipse ...

  7. Cesium案例解析(九)——Rotatable2DMap旋转2D地图

    目录 Cesium的Rotatable 2D Map示例展示了一个旋转的二维地图: 'use strict'; var viewer = new Cesium.Viewer('cesiumContai ...

  8. MySQL进阶篇:详解存储引擎介绍和基本使用

    1. MySQL进阶篇:第一章__一.一_存储引擎介绍和基本使用 1.1 MySQL体系结构 1). 连接层 最上层是一些客户端和链接服务,包含本地sock 通信和大多数基于客户端/服务端工具实现的类 ...

  9. DataLeap的Catalog系统近实时消息同步能力优化

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 摘要 字节数据中台DataLeap的Data Catalog系统通过接收MQ中的近实时消息来同步部分元数据.Apa ...

  10. Solon Web 开发:二、开发知识准备

    1.约定 //资源路径约定(不用配置:也不能配置) resources/app.yml( 或 app.properties ) #为应用配置文件 resources/WEB-INF/static/ 或 ...