一文搞懂Spring Boot 配置加载原理以及配置优先级

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
简介: 背景:最近有个开发同学问我,为什么他在配置文件里面配置了端口号但是一直不生效,我看了后发现在其他地方已经配置过端口号了,所以当前配置不生效,那么到底Spring Boot 是如何处理配置文件的呢,在配置项重复的情况下又是如何处理的呢
  • 👏作者简介:大家好,我是冰点,从业11年,目前在物流独角兽企业从事技术方面工作,
  • 🍂博主正在努力完成2023计划中:以梦为马,扬帆起航,2023追梦人
  • 📝联系方式:iceicepip,加我进群,大家一起学习,一起进步👀

@[TOC]

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 关于配置优先级的官方文档

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

目录
相关文章
|
8天前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
|
21天前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
11天前
|
Java 应用服务中间件 API
Vertx高并发理论原理以及对比SpringBoot
Vertx 是一个基于 Netty 的响应式工具包,不同于传统框架如 Spring,它的侵入性较小,甚至可在 Spring Boot 中使用。响应式编程(Reactive Programming)基于事件模式,通过事件流触发任务执行,其核心在于事件流 Stream。相比多线程异步,响应式编程能以更少线程完成更多任务,减少内存消耗与上下文切换开销,提高 CPU 利用率。Vertx 适用于高并发系统,如 IM 系统、高性能中间件及需要较少服务器支持大规模 WEB 应用的场景。随着 JDK 21 引入协程,未来 Tomcat 也将优化支持更高并发,降低响应式框架的必要性。
Vertx高并发理论原理以及对比SpringBoot
|
11天前
|
XML 存储 Java
spring源码刨析-spring-beans(内部核心组件,beanDefinition加载过程)
spring源码刨析-spring-beans(内部核心组件,beanDefinition加载过程)
|
22天前
|
Java 开发者 数据格式
【Java笔记+踩坑】SpringBoot基础4——原理篇
bean的8种加载方式,自动配置原理、自定义starter开发、SpringBoot程序启动流程解析
【Java笔记+踩坑】SpringBoot基础4——原理篇
消息中间件 缓存 监控
81 0
|
1月前
|
IDE Java 开发工具
还在为繁琐的配置头疼吗?一文教你如何用 Spring Boot 快速启动,让开发效率飙升,从此告别加班——打造你的首个轻量级应用!
【9月更文挑战第2天】Spring Boot 是一款基于 Spring 框架的简化开发工具包,采用“约定优于配置”的原则,帮助开发者快速创建独立的生产级应用程序。本文将指导您完成首个 Spring Boot 项目的搭建过程,包括环境配置、项目初始化、添加依赖、编写控制器及运行应用。首先需确保 JDK 版本不低于 8,并安装支持 Spring Boot 的现代 IDE,如 IntelliJ IDEA 或 Eclipse。
87 5
|
10天前
|
消息中间件 NoSQL 安全
(转)Spring Boot加载 不同位置的 application.properties配置文件顺序规则
这篇文章介绍了Spring Boot加载配置文件的顺序规则,包括不同位置的application.properties文件的加载优先级,以及如何通过命令行参数或环境变量来指定配置文件的名称和位置。
|
2月前
|
Java Spring 开发者
解锁 Spring Boot 自动化配置的黑科技:带你走进一键配置的高效开发新时代,再也不怕繁琐设置!
【8月更文挑战第31天】Spring Boot 的自动化配置机制极大简化了开发流程,使开发者能专注业务逻辑。通过 `@SpringBootApplication` 注解组合,特别是 `@EnableAutoConfiguration`,Spring Boot 可自动激活所需配置。例如,添加 JPA 依赖后,只需在 `application.properties` 配置数据库信息,即可自动完成 JPA 和数据源设置。这一机制基于多种条件注解(如 `@ConditionalOnClass`)实现智能配置。深入理解该机制有助于提升开发效率并更好地解决问题。
49 0
|
2月前
|
缓存 Java 数据库连接
Spring Boot 资源文件属性配置,紧跟技术热点,为你的应用注入灵动活力!
【8月更文挑战第29天】在Spring Boot开发中,资源文件属性配置至关重要,它让开发者能灵活定制应用行为而不改动代码,极大提升了可维护性和扩展性。Spring Boot支持多种配置文件类型,如`application.properties`和`application.yml`,分别位于项目的resources目录下。`.properties`文件采用键值对形式,而`yml`文件则具有更清晰的层次结构,适合复杂配置。此外,Spring Boot还支持占位符引用和其他外部来源的属性值,便于不同环境下覆盖默认配置。通过合理配置,应用能快速适应各种环境与需求变化。
35 0
下一篇
无影云桌面