三分钟了解SpringBoot配置优先级底层源码解析

news2024/12/26 21:39:15
  • 👏作者简介:大家好,我是冰点,从业11年,目前在物流独角兽企业从事技术方面工作,
  • 🍂博主正在努力完成2023计划中:以梦为马,扬帆起航,2023追梦人
  • 📝联系方式:iceicepip,加我进群,大家一起学习,一起进步👀

文章目录

  • 1. 前言
  • 2. 结论
  • 2. 配置文件的加载流程
    • 2.1 确定需要加载哪些配置文件
    • 2.1 加载配置文件
    • 2.1 排序配置文件
    • 2.1 合并属性
    • 2.1 绑定属性到Java对象
  • 3. 核心源码解析
      • 3.1 配置加载相关类
      • 3.2 属性注入和绑定的源码
  • 4.总结

1. 前言

背景:最近有个开发同学问我,为什么他在配置文件里面配置了端口号但是一直不生效,我看了后发现在其他地方已经配置过端口号了,所以当前配置不生效,那么到底Spring Boot 是如何处理配置文件的呢,在配置项重复的情况下又是如何处理的呢

在Spring Boot中,可以使用.properties或.yml文件来配置应用程序属性。默认情况下,Spring Boot将首先查找application.properties或application.yml文件,如果没有找到,则会使用其他命名规则的文件,例如application-{profile}.properties或application-{profile}.yml,其中{profile}是激活的Spring配置文件。
其实在SpringBoot中我们不仅仅可以通过配置文件进行配置,也可以有以下这几种

  1. 命令行参数 可以使用–property=value语法在命令行中指定属性值。
  2. Java系统属性(System.getProperties())。
  3. 操作系统环境变量。

2. 结论

在这里插入图片描述

  1. 命令行参数 > 2. Java系统属性 >3. 操作系统环境变量>4.jar包外部的application>5.jar包内部的application- > 5.jar包外部的application.properties > 7.jar包内部的application.properties

根据结论我们先抛出几个问题
1. spring boot 在application.properties 和application.yml中如果有相同的配置项,谁的优先级更高呢
2. 上面的结论里面为什么没有bootstrap.properties和bootstrap.yml。那么他的优先级是位于哪里呢?

2. 配置文件的加载流程

2.1 确定需要加载哪些配置文件

Spring Boot默认会加载application.properties或application.yml文件,如果当前激活了某个profile,还会额外加载application-{profile}.properties或application-{profile}.yml文件。

例如,如果激活了"dev" profile,那么Spring Boot会加载application.properties和application-dev.properties两个文件,并将它们合并成一个属性集合。

2.1 加载配置文件

Spring Boot使用ResourceLoader接口来加载配置文件。ResourceLoader是一个抽象接口,它定义了一组方法,用于加载各种类型的资源,包括文件、URL、类路径等等。在Spring Boot中,通常使用ClasspathResourceLoader来加载配置文件。
配置文件的加载主要是这两个核心类。如果大家感兴趣可以点进去,就会发现,spring boot 不仅仅只支持.properties 和yml 后缀的配置文件。它同样也支持xml、yaml。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.1 排序配置文件

Spring Boot使用Ordered接口来排序配置文件,以确保它们被正确地解析和合并。每个配置文件都可以实现Ordered接口来指定它们的优先级。Spring Boot默认会按照以下顺序排序配置文件:
bootstrap.properties
application.properties
application.yml
application-{profile}.properties
application-{profile}.yml
其中,application.properties和application.yml的优先级最低,而激活的profile相关的配置文件的优先级最高。

  1. bootstrap.properties:如果存在bootstrap.properties文件,则其中的属性将具有比其他属性文件更高的优先级。主要用于配置SpringCloud Config Server连接属性等。
  2. application.properties:如果存在则其中的属性将被加载,并覆盖与之相同的属性文件中的属性。
  3. application.yml:如果存在application.yml文件,则其中的属性将被加载,并覆盖与之相同的属性文件中的属性。
  4. application-{profile}.properties:Spring Boot会根据当前激活的配置文件加载与之对应的属性文件。例如,如果激活了dev配置文件,则会加载application-dev.properties文件。如果存在application-{profile}.properties文件,则其中的属性将被加载,并覆盖与之相同的属性文件中的属性。
  5. application-{profile}.yml:如果存在application-{profile}.yml文件,则其中的属性将被加载,并覆盖与之相同的属性文件中的属性。如果同时存在application-{profile}.properties和application-{profile}.yml文件,则以.properties文件为准。

如果存在多个属性文件中具有相同的属性,则具有最高优先级的属性将覆盖其他属性。例如,如果在application.properties文件和application-dev.properties文件中都定义了server.port属性,则application-dev.properties文件中定义的属性将覆盖application.properties文件中的属性。同样,如果在application.yml和application-dev.yml文件中都定义了相同的属性,则application-dev.yml文件中定义的属性将覆盖application.yml文件中的属性

2.1 合并属性

在排序和加载完所有配置文件之后,Spring Boot将它们合并成一个统一的属性集合。如果有多个配置文件中都定义了相同的属性,则后加载的配置文件中的属性会覆盖之前加载的属性。

2.1 绑定属性到Java对象

最后,Spring Boot使用@ConfigurationProperties注解将属性集合绑定到Java对象上。这个注解告诉Spring Boot将属性集合中的属性自动绑定到Java对象的相应属性上。当Java对象被创建时,Spring Boot会自动将属性集合中的属性值注入到该对象的属性中。这使得开发人员可以轻松地访问和使用配置属性。

3. 核心源码解析

ConfigFileApplicationListener类(spring boot 2.4.0之后版本已经废弃,之后版本使用ConfigDataEnvironmentPostProcessor)是Spring Boot中负责加载应用程序配置文件并将其转换为统一属性集合的关键组件之一。该类监听了ApplicationEnvironmentPreparedEvent事件,当事件发生时,它会加载应用程序的配置文件,并将其合并为一个统一的属性集合。

在onApplicationEvent方法中,它首先判断事件类型,如果是ApplicationEnvironmentPreparedEvent事件,则调用onApplicationEnvironmentPreparedEvent方法进行处理。在onApplicationEnvironmentPreparedEvent方法中,它首先调用loadPostProcessors方法加载EnvironmentPostProcessor对象,并将ConfigFileApplicationListener自身添加到postProcessors列表中。然后,通过AnnotationAwareOrderComparator类对postProcessors列表进行排序,以保证EnvironmentPostProcessor对象按照指定的顺序执行。最后,遍历postProcessors列表,调用每个EnvironmentPostProcessor对象的postProcessEnvironment方法,将配置文件中的属性合并到Spring的Environment中。

可以参考代码注释理解

@Override
public void onApplicationEvent(ApplicationEvent event) {
    // 如果是ApplicationEnvironmentPreparedEvent事件,则调用onApplicationEnvironmentPreparedEvent方法处理
    if (event instanceof ApplicationEnvironmentPreparedEvent) {
        onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
    }
    // 如果是ApplicationPreparedEvent事件,则调用onApplicationPreparedEvent方法处理
    if (event instanceof ApplicationPreparedEvent) {
        onApplicationPreparedEvent(event);
    }
}

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
    // 加载EnvironmentPostProcessor对象,并将ConfigFileApplicationListener自身添加到postProcessors列表中
    List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
    postProcessors.add(this);
    // 对postProcessors列表进行排序,以保证EnvironmentPostProcessor对象按照指定的顺序执行
    AnnotationAwareOrderComparator.sort(postProcessors);
    // 遍历postProcessors列表,调用每个EnvironmentPostProcessor对象的postProcessEnvironment方法,将配置文件中的属性合并到Spring的Environment中
    for (EnvironmentPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
    }
}

排序后,我们可以看到这块的一共有7个配置加载处理器。我们只关注配置文件的处理。此处我们也能看到如果引入了Apollo。Apollo配置处理器的加载顺序。
在这里插入图片描述
如果我们跟process的执行逻辑,其实最后都是走到了配置加载方法

public void load() {
    // 初始化profiles、processedProfiles、activatedProfiles和loaded等属性
    this.profiles = new LinkedList<>();
    this.processedProfiles = new LinkedList<>();
    this.activatedProfiles = false;
    this.loaded = new LinkedHashMap<>();
    // 初始化profiles属性,表示需要处理的配置文件的profile
    initializeProfiles();
    // 处理profiles属性中的各个profile
    while (!this.profiles.isEmpty()) {
        // 从profiles队列中取出一个Profile对象
        Profile profile = this.profiles.poll();
        // 如果profile不为空且不是默认profile,则将其添加到Environment中
        if (profile != null && !profile.isDefaultProfile()) {
            addProfileToEnvironment(profile.getName());
        }
        // 加载profile对应的配置文件
        load(profile, this::getPositiveProfileFilter,
                addToLoaded(MutablePropertySources::addLast, false));
        // 将processedProfiles属性中添加已处理的profile
        this.processedProfiles.add(profile);
    }
    // 加载不属于任何profile的配置文件
    load(null, this::getNegativeProfileFilter,
            addToLoaded(MutablePropertySources::addFirst, true));
    // 将loaded属性中的属性源添加到Environment中
    addLoadedPropertySources();
}

从ConfigFileApplicationListener中我们能够看到有哪些地方我们可以放配置文件。
在这里插入图片描述
上面的源码基本上能解决我们Spring Boot 是如何加载,解析配置文件的。但是解决不了我们这些配置项是如何绑定到对象的配置类(@Configration)的属性上的,或者如何注入到@Value 的注解修饰的属性上的。
由于篇幅有限,我大概抛转引玉,为大家抛砖引玉,解释一下关于Spring Boot 配置加载和解析,绑定相关的类。
和配置加载相关的类位于spring-boot包下

3.1 配置加载相关类

在这里插入图片描述

3.2 属性注入和绑定的源码

位于 org.springframework.boot.context.properties

在这里插入图片描述
我们可以看到有我们熟悉的,可能使用过的源码或注解
ConfigurationProperties:注解用于标记一个类,表示它是一个配置属性类,用于存储应用程序的配置信息。
ConfigurationPropertiesBinder:用于将配置属性绑定到 对象上。
ConfigurationPropertiesBindingPostProcessor 容器启动时用于处理带有@ConfigurationProperties注解的 Bean。

4.总结

所以我们通常遇到问题如果发现很奇怪无法解释,可能只需要翻翻官方文档,跟跟代码就可以解释了。此代码版本为2.0版本。其他版本会有差异,但是原理上都是大同小异的。再教给大家一个看官方文档的小技巧。比如我要看Spring Boot 2.0.3版本文档,只需要将URL 。https://docs.spring.io/spring-boot/docs/2.0.3.RELEASE/reference/htmlsingle/中的版本号改为自己当前项目的版本号即可。
或者干脆到Spring Boot 文档索引里面查看https://docs.spring.io/spring-boot/docs/

Spring boot 关于配置优先级的官方文档

好了,本次的分享就到这儿,我是冰点,下次见。如果我的文章对你有所收获,可以给个赞。如果有疑问可以在评论区留言。

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

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

相关文章

关于性能测试平台的一些想法,想跟大家聊一下

目录 一、任务管理 二、用例管理 三、环境管理 四、压测机管理 五、数据管理 六、监控管理 七、日志管理 八、报表管理 九、配置管理 十、系统管理 组织架构 这里我按照每个不同系统归属的项目组为横向&#xff0c;性能测试团队作为职能部门为纵向的矩阵式组织架构为…

JUC学习(二)

目录 Doug Lea — JUC并发包的作者锁框架Lock和Condition接口可重入锁公平锁与非公平锁读写锁锁降级和锁升级队列同步器AQS底层实现公平锁一定公平吗&#xff1f;Condition实现原理 ——————————————————————————————— 在前面&#xff0c;我们了解…

ICV报告:乘光伏新能源汽车之势,功率器件蓄势待发

前言&#xff1a; 电力电子器件&#xff08;Power Electronic Device&#xff09;&#xff0c;又称为功率半导体器件&#xff0c;用于电能变换和电能控制电路中的大功率(通常指电流为数十至数千安&#xff0c;电压为数百伏以上)电子器件。功率器件能够承受和控制较大电流、电压…

无限阳光、自动收集阳光CALL、阳光产生速度

简单实现无限阳光 本次实验内容&#xff1a;通过逆向分析植物阳光数量的动态地址找到阳光的基址与偏移&#xff0c;从而实现每次启动游戏都能够使用基址加偏移的方式定位阳光数据&#xff0c;最后我们将通过使用C语言编写通用辅助实现简单的无限阳光外挂&#xff0c;在教程开始…

Vue Router路由管理器

目录&#xff1a; 相关理解基本路由几个注意事项嵌套&#xff08;多级&#xff09;路由路由的query参数命名路由路由的params参数路由的props配置路由跳转的replace方法编程式路由导航缓存路由组件activated和deactivated路由守卫路由器的两种工作模式 相关理解 vue-route…

博学谷学习记录】超强总结,用心分享 | 架构师 敏捷开发 学习总结

文章目录 敏捷开发1. 概述2. 敏捷开发 敏捷开发 1. 概述 随着软件开发技术的不断发展&#xff0c;现在出现了很多种不同的开发模式&#xff0c;其实敏捷开发已经成为现在很多企业开发应用程序都想要选择的开发方案&#xff0c;那么什么是敏捷开发呢&#xff1f;1.1 四种开发模…

Linux 配置Java环境(一)

Linux 配置Java环境 一、配置Java环境1、查看系统是否有java环境2、卸载系统自带的jdk3、创建一个文件夹用于存放java的压缩包4、包下载好的jdk拖到java文件夹5、安装jdk6、配置环境变量7、让配置生效8、验证是否配置成功 一、配置Java环境 1、查看系统是否有java环境 输入指…

nginx中location和rewrite

常用的Nginx 正则表达式 ^ &#xff1a;匹配输入字符串的起始位置 $ &#xff1a;匹配输入字符串的结束位置 * &#xff1a;匹配前面的字符零次或多次。如“ol*”能匹配“o”及“ol”、“oll” &#xff1a;匹配前面的字符一次或多次。如“ol”能匹配“ol”及“oll”、“olll…

0-1背包问题:动态规划的经典应用

文章目录 引言背包问题简介0-1背包问题定义0-1背包问题的限制条件 动态规划解决思路状态定义状态转移方程 背包问题的Java实现示例与分析 总结 引言 背包问题是在给定一组物品和一个背包容量的情况下&#xff0c;如何选择物品放入背包&#xff0c;以使得放入背包的物品总价值最…

高边功率开关参数Load current(ISO)和Nominal current

1. IL(nom)是没有加散热片的情况下&#xff0c;考虑RON和BTS6133D与环境热阻计算得到的电流值&#xff1b; 2. IL(iso)是有散热片的情况下计算得到的电流值&#xff1b; 3. IL12(SC)是如果负载电流达到75A以上&#xff0c;BTS6143D会通过不断重启来来限制电流在75A以下。

【论文简述】GeoMVSNet: Learning Multi-View Stereo with Geometry Perception(CVPR 2023)

一、论文简述 1. 第一作者&#xff1a;Jie Zhu 2. 发表年份&#xff1a;2023 3. 发表期刊&#xff1a;CVPR 4. 关键词&#xff1a;MVS、级联结构、几何感知、频域增强、高斯混合模型 5. 探索动机&#xff1a;基于级联的结构以从粗到细的方式计算不同分辨率的深度图&#x…

数据结构与算法练习(二)数组模拟队列

文章目录 1、队列2、数组实现队列3、数组实现循环队列 1、队列 队列是一个有序列表&#xff0c;可以用数组或链表来实现 遵循先入先出的原则 在队尾插入元素叫做入队&#xff0c;对头删除元素叫做出队。2、数组实现队列 思路&#xff1a; 用front和rear记录队列前后的下标&a…

【MCS-51单片机汇编语言】期末复习总结⑤——定时器中断方式与查询方式程序设计(题型五)

文章目录 知识准备工作方式寄存器TMODTCON寄存器IE寄存器 定时/计数器的使用初始化 常考题型例题1题目描述题目解析题解 例题2题目描述题目解析题解 知识准备 工作方式寄存器TMOD D7D6D5D4D3D2D1D0TF1TR1TF0TR0IE1IT1IE0IT0 [注]&#xff1a;TMOD高4位与T1相关&#xff0c;低…

iptables和防火墙

文章目录 1.防火墙2.Iptables基本介绍2.1 什么是iptables2.2 什么是包过滤防火墙2.3 包过滤防火墙如何实现 1.防火墙 Linux防火墙主要工作在网络层&#xff0c;针对 TCP/IP 数据包实施过滤和限制&#xff0c;典型的包过滤防火墙&#xff0c;基于内核编码实现&#xff0c;具有非…

Qt(C++)绘制指针仪表盘显示当前温度

一、功能介绍 当前文章要实现的功能: 使用Qt绘制一个仪表盘,用来显示当前的温度,绘制刻度、绘制数字、绘制温度指针。仪表盘全程使用QPainter进行绘制,QPainter是Qt框架中非常重要的一个类,绘制功能的实现离不开它。如果想要使用Qt进行高质量的绘图或UI设计,必须掌握QP…

Groovy 基本语法

一、简介 类型转换:当需要时,类型之间会自动发生类型转换: 字符串&#xff08;String&#xff09;、基本类型(如int) 和类型的包装类(如Integer) 类说明&#xff1a;如果在一个groovy 文件中没有任何类定义&#xff0c;它将被当做script 来处理&#xff0c;也就意味着这个文件将…

DragGAN-点对点的手动P图

原文网站link以及论文链接&#xff1a; https://vcai.mpi-inf.mpg.de/projects/DragGAN/ https://arxiv.org/pdf/2305.10973.pdf 简介 深度生成模型近年在合成随机真实感图像方面取得了不错的成绩。在现实世界的应用中&#xff0c;这种基于学习的图像合成方法中一个关键功能是…

文心一言 VS 讯飞星火 VS chatgpt (31)-- 算法导论5.2 3题

三、利用指示器随机变量来计算掷n 个骰子之和的期望值。 文心一言&#xff1a; 为了计算掷n个骰子之和的期望值&#xff0c;我们需要先了解一个重要的概念&#xff1a;指示器随机变量。 指示器随机变量是一种特殊的随机变量&#xff0c;它只有两个取值&#xff1a;0和1。通常…

《计算机网络——自顶向下方法》精炼——3.7(2)

读书有三到&#xff1a;谓心到&#xff0c;眼到&#xff0c;口到。——明朱熹 文章目录 对链接吞吐量的简化描述高带宽路径的TCP公平性 对链接吞吐量的简化描述 为了简化对一条TCP连接吞吐量的描述&#xff0c;我们首先忽略连接过程中处于慢启动状态的时间&#xff0c;因为这一…

AWD练习平台搭建 附虚拟机资源

文章目录 前言基本环境平台搭建系统安装平台部署环境启动实现效果 参考后记 前言 最近需要准备线下AWD了&#xff0c;但是没有那么多金币每天上BugKu的PVP在线AWD对战进行练习&#xff0c;于是想着可以自己搭建一个团队内部使用的AWD攻防平台&#xff0c;浅浅的记录一下 基本…