Spring Boot 3.0.x 自动配置文件加载原理

news2025/2/24 12:20:09

我们知道 Spring Boot 开启自动配置使用的是 @EnableAutoConfiguration 注解,一般使用组合了它的 @SpringBootApplication 主注解即可。那么 Spring Boot 是如何加载包含了各种需要自动配置的类的配置文件的呢?本文我们基于 Spring Boot 3.0.6 一起看看 Spring Boot 自动配置文件加载的原理。

@EnableAutoConfiguration 注解的源码如下:

/**
 * 启用Spring应用程序上下文的自动配置,尝试猜测和配置您可能需要的bean。
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited // 允许子类继承
@AutoConfigurationPackage // 注解会将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器
@Import(AutoConfigurationImportSelector.class) // 导入组件,将所有需要导入的组件以全类名的方式返回,这些组件就会被添加到容器中
public @interface EnableAutoConfiguration {

	/**
	 * 环境属性,可以用来覆盖自动配置是否启用。
	 */
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * 用于排除某些自动配置类,被排出的自动配置类不会被加载到容器中。
	 */
	Class<?>[] exclude() default {};

	/**
	 * 根据类名排除自动配置类,被排除的自动配置类不会被加载到容器中。
	 */
	String[] excludeName() default {};
}

上面最关键的两个注解是:

  • @AutoConfigurationPackage:用于自动配置启用时扫描指定类所在的包及其子包中的所有类。该注解通常用于在应用程序入口类上使用,以便自动配置能够自动扫描应用程序所需的包。具体来说,该注解会将指定类所在的包及其子包中的所有类的所在包路径作为自动配置的 basePackage,然后将其注册到 Spring 的 BeanFactory 中,以便自动配置能够扫描这些包中的所有类。
  • @Import:导入配置类,这里导入的就是其中的 ImportSelector 接口实现。

自动配置 @Import 注解导入的是 AutoConfigurationlmportSelector.class 类,这也是这个注解的关键所在,它实现了 ImportSelector 接口。Spring Boot 中的 ImportSelector 接口是一个用于动态导入配置类的接口,它可以根据条件动态地选择需要导入的配置类。

在 Spring Boot 中,我们可以使用 @Import 注解来导入配置类,但是如果需要根据条件来动态选择导入哪些配置类,就需要使用 ImportSelector 接口。

ImportSelector 接口源码如下:

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata importingClassMetadata);

    @Nullable
    default Predicate<String> getExclusionFilter() {
        return null;
    }
}

该接口包含了两个方法:

  • selectImports

    该方法是 ImportSelector 接口的的核心方法,用于从传入的 AnnotationMetadata 中选择要导入的配置类。在该方法中,可以根据 AnnotationMetadata importingClassMetadata 中的注解信息来决定需要导入哪些配置类。返回一个 String 数组,其中包含要导入的配置类的全限定类名。

  • getExclusionFilter

    该方法用于提供一个谓词函数,用于过滤掉一些不需要导入的配置类。该函数接受一个字符串参数(即要导入的配置类的全限定类名),并返回一个布尔值,指示该配置类是否需要被排除。如果不需要进行任何过滤,可以返回 null 或一个恒返回 false 的谓词函数。

然后我们回到 AutoConfigurationlmportSelector 类,它的作用是根据类路径下的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中定义的自动配置类来动态导入配置类。

AutoConfigurationlmportSelector 类实现的 ImportSelector 接口的两个方法源码如下:

public String[] selectImports(AnnotationMetadata annotationMetadata) {
    // 检查是否启用自动配置
    if (!this.isEnabled(annotationMetadata)) {
        // 如果未启用,则返回空数组
        return NO_IMPORTS;
    } else {
        // 获取自动配置条目
        AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
        // 返回自动配置条目中的配置类的字符串数组
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

public Predicate<String> getExclusionFilter() {
    // 返回一个谓词函数,用于过滤应该排除的自动配置类
    return this::shouldExclude;
}

进入 getAutoConfigurationEntry 方法,该方法用于用于获取自动配置条目,方法源码如下:

protected AutoConfigurationEntry getAutoConfigurationEntry(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.getConfigurationClassFilter().filter(configurations);
        // 触发自动配置导入事件
        this.fireAutoConfigurationImportEvents(configurations, exclusions);
        // 构造自动配置条目并返回
        return new AutoConfigurationEntry(configurations, exclusions);
    }
}

我们将关注点聚集在 getCandidateConfigurations 方法,该方法用于获取候选的自动配置类。方法源码如下:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    // 从 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中加载所有自动配置类
    List<String> configurations = ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).getCandidates();
    // 检查自动配置类列表是否为空
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
    // 返回自动配置类列表
    return configurations;
}

其中,ImportCandidates.load 方法是从指定的位置加载所有实现了指定接口的类,并返回一个 ImportCandidate 实例,用于访问加载的类。在这里,该方法用于加载所有实现了 AutoConfiguration 接口的类。this.getBeanClassLoader() 用于获取当前线程的上下文类加载器,用于加载类。如果自动配置类列表为空,则会抛出一个 IllegalStateException 异常,提示用户检查自动配置文件是否正确。

上面提到的 ImportCandidates 类是一个辅助类,用于加载所有实现了指定接口的候选类。它有一个 load 方法,用于加载所有实现了指定接口的类,并返回一个 ImportCandidates 实例。

方法源码如下:

public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
    // 检查注解类是否为空
    Assert.notNull(annotation, "'annotation' must not be null");
    // 获取要使用的类加载器
    ClassLoader classLoaderToUse = decideClassloader(classLoader);
    // 获取要加载的自动配置类列表所在的位置
    String location = String.format("META-INF/spring/%s.imports", annotation.getName());
    // 在类路径中查找指定位置的资源
    Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
    // 保存所有候选类的列表
    List<String> importCandidates = new ArrayList();
    // 遍历所有找到的资源
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        // 读取资源中的所有候选类,并添加到列表中
        importCandidates.addAll(readCandidateConfigurations(url));
    }
    // 返回一个包含所有候选类的 ImportCandidates 实例
    return new ImportCandidates(importCandidates);
}

从源码中我们可以看到,Spring Boot 3.0.x 的自动配置逻辑是加载 META-INF/spring/%s.imports 配置文件中的自动配置,这个 %s 是占位符,指 @AutoConfiguration 注解的类的全路径名称,如下图所示:

该配置文件内容如下:

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
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration
...... Spring Boot 3.0.6 共注册了142个自动配置类

可以看到,编写格式确实简单了,一些应用级别的自动配置类已经全移到这个新配置文件中了。而老的自动配置文件 (spring.factories) 还保留了一些系统级别的组件,如各种初始化器、监听器等,这些初始化器和监听器都会在 Spring Boot 启动时完成自动配置。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/483393.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ChatGLM实战 - 文本信息抽取

1. ChatGLM介绍 ChatGLM-6B 是一个开源的、支持中英双语的对话语言模型&#xff0c;基于 General Language Model (GLM) 架构&#xff0c;具有 62 亿参数。结合模型量化技术&#xff0c;用户可以在消费级的显卡上进行本地部署&#xff08;INT4 量化级别下最低只需 6GB 显存&…

微服务之事务处理

Informal Essay By English Hi guys、happy labor day. Everyone should have a good time to relax during the Labor Day holiday. But don’t forget to improve yourself during the holiday period 参考书籍&#xff1a; “凤凰架构” “微服务架构设计模式” 引言 …

K8S[Kubernetes]快速安装组件(Kubectl Kubeadam Kubeinit)

文章目录 配置K8S主从集群前置准备操作一&#xff1a;主节点操作 查看主机域名->编辑域名1.1 编辑HOST 从节点也做相应操作1.2 从节点操作 查看从节点102域名->编辑域名1.3 从节点操作 查看从节点103域名->编辑域名 二&#xff1a;安装自动填充&#xff0c;虚拟机默认…

汇编语言学习笔记四

字符 字符是以ASCII码的形式存储的&#xff0c;一个字符对应着8为二进制数&#xff0c;2位16进制数。 所以可以得到对应的字符地址。 assume ds:data data segmentdb hellodb world data endsand or指令 根据ASCII码&#xff0c;字符的大写和小写相差一个0010 0000&#xff…

Java的锁事

乐观锁和悲观锁 悲观锁 认为自己在使用数据的使用一定有别的线程来修改数据&#xff0c;因此在获取数据的时候会先加锁&#xff0c;确保数据不会被别的线程修改 synchronized关键字和Lock的实现都是悲观锁 适合写操作多的场景&#xff0c;先加锁可以保证写操作时数据正确 …

SpringcloudAlibaba详解

目录 微服务架构概念 服务治理 服务调用 服务网关 服务容错 链路追踪 SpringcloudAlibaba组件 Nacos 负载均衡 Ribbon Fegin Sentinel 高并发测试 容错方案 Sentinel入门 Feign整合Sentinel 微服务架构概念 服务治理 服务治理就是进行服务的自动化管理&#xf…

解决使用git命令查看的某次提交时间与git log中显示的不一致的问题

文章目录 问题描述缘由解决办法参考资料 问题描述 笔者的使用环境&#xff1a; Git 2.37.0.windows.1 TortoiseGit 2.11.0.0 IntelliJ IDEA 2022.3.1 (Ultimate Edition) 笔者遇到一个问题&#xff0c;需要查看 Git 某个提交&#xff08;commit&#xff09;的时间&#xff0…

女朋友说总是记不住Git命令,怎么办?安排!

如果你也和我女朋友一样总是忘记Git命令&#xff0c;觉得记忆Git命令是很枯燥和麻烦的事情。我写了一个包含了40 条常用Git命令的清单。你一定要收藏起来&#xff0c;当你忘记Git命令的时候&#xff0c;就可以打开来查看啦&#xff01;&#xff01;&#xff01; 1.初始化本地仓…

程序员实习和就业选择应该怎么做?分享4个重要的影响因素

近期后台很多人都有问到找实习、找工作相关的问题&#xff0c;今天就展开聊聊。首先&#xff0c;理个时间点给大家提个醒&#xff0c;暑期实习的简历投递时间集中在3月-5月&#xff0c;秋招简历投递时间集中在6月-10月。所以&#xff0c;今年想找实习、想投秋招的学生一定要注意…

MySQL的概念,编译及安装

一.数据库的基本概念 1、数据&#xff08;Data&#xff09; • 描述事物的符号记录 • 包括数字&#xff0c;文字&#xff0c;图形&#xff0c;图像&#xff0c;声音&#xff0c;档案记录等 • 以“记录”形式按统一的格式进行存储 2、表 • 将不同的记录组织在一起 • …

WiFi也能检测人体3D动作?误差低至2.4厘米

佛罗里达州立大学和罗格斯大学的科研人员开发了一种基于Wi-Fi传感的3D网格技术&#xff1a;Wi-Mesh&#xff0c;该方案宣称可识别并创建可靠的3D人体网格&#xff0c;可辅助计算机视觉、AR/VR定位等应用。 简单来讲&#xff0c;Wi-Mesh通过分析多个Wi-Fi天线接收的反射信号&…

ubuntu20安装electron并用electron-packager打包

起因是想复现港科大的无人机集群GUI界面&#xff1a;https://github.com/HKUST-Aerial-Robotics/swarm_gcs 这是一个用javascript写的&#xff0c;基于electron和nodejs的可以用浏览器打开的gui界面&#xff0c;其中3d显示的部分用的是Three.js。electron有一个好处是可以跨平台…

【MySQL学习】MySQL数据类型

文章目录 前言一、数据类型分类二、数值类型2.1 TINYINT类型2.2 BIT类型2.3 小数类型2.3.1 float类型2.3.2 decimal类型 三、字符串类型3.1 CHAR类型3.2 VARCHAR类型3.3 CHAR和VARCHAR比较 四、日期和时间类型五、ENUM和SET类型 前言 MySQL 数据类型定义了列中可以存储什么数据…

串口信息打印规范(含打印技巧)

1.串口信息打印规范 学习一下串口打印信息的格式&#xff08;清楚明了&#xff0c;调试过程中很重要&#xff09; 日志级别&#xff1a;info&#xff08;初始化&#xff09;、debug&#xff08;运行过程&#xff09;、error&#xff08;报错&#xff09; [日志级别] 文件名 …

二十一、SQL 数据分析基础与进阶:窗口函数

文章目录 一、什么是窗口函数二、聚合函数OVER()函数三、PARTITION BY 子句四、排序函数4.1 ROW_NUMBER() 函数4.2 演示 RANK()、DENSE_RANK()、ROW_NUMBER() 函数的异同4.3 NTILE() 函数4.4 LAG() 和 LEAD() 函数4.5 FIRST_VALUE() 和 LAST_VALUE() 函数 五、Window Frames 自…

S5CL:通过分层对比学习统一全监督、自监督和半监督学习

文章目录 S5CL: Unifying Fully-Supervised,Self-supervised, and Semi-supervised Learning Through Hierarchical Contrastive Learning摘要本文方法损失函数 实验结果消融实验 S5CL: Unifying Fully-Supervised,Self-supervised, and Semi-supervised Learning Through Hier…

计算机体系结构框架

这是基于胡伟武老师的计算机体系结构课程所总结出来的框架&#xff0c;希望能让没有学习该课程的人可以去了解计算机是怎么造的&#xff0c;而对于学习这门课程的人可以在学习课程之前对整体框架有一个初步的认知。 如果不想看文字的话&#xff0c;可以看视频哦&#xff01; 目…

ros2 服务——ubuntu20.04——自定义数据类型

文章目录 自定义一个服务数据类型接口创建sev目录和文件修改包的CMakeLists.txt文件修改包的package.xml文件查看是否成功 服务全部代码 自定义一个服务数据类型接口 创建sev目录和文件 服务的接口类型由两部分组成&#xff0c;请求和相应 在包的src的同级目录下创建sev文件…

OpenCV基础补充自适应阈值及图像金字塔

文章目录 OpenCV基础补充自适应阈值及图像金字塔自适应阈值图像金字塔人脸检测视频检测人脸检测 OpenCV基础补充自适应阈值及图像金字塔 对于OpenCV知识点还有很多&#xff0c;基础的大家可以参考前面几节。 OpenCv基础之绘图及几何变换实例 OpenCV基础操作之图像的形态学运算…

PMP/高项 06-项目成本管理

项目成本管理 概念 项目成本管理 项目成本管理又被称为项目造价管理&#xff0c;是有关项目成本和项目价值两个方面的管理&#xff0c;是为保障以最小的成本实现最大的项目价值而开展的项目专项管理工作。 确保在批准的项目预算内完成项目 成本管理内容 规划成本管理 制定项目…