乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • 学习路径
  • 第1章 Spring Boot 概述

    • 章节导读:Spring Boot概述与核心理念
    • Spring Boot是什么
    • Spring Boot与Spring Framework的关系
    • 约定优于配置
  • 第2章 快速入门与第一个应用

    • 章节导读:快速入门与第一个应用
    • SpringApplication
    • 第一个Spring Boot应用
  • 第3章 起步依赖与版本管理

    • 章节导读:起步依赖与版本管理
    • 起步依赖
    • BOM版本管理
  • 第4章 自动配置原理

    • 章节导读:自动配置原理
    • 自动配置原理
    • AutoConfigurationImportSelector
    • 自动配置报告
  • 第5章 核心注解

    • 章节导读:核心注解
    • @SpringBootApplication
    • @EnableAutoConfiguration
    • @ConditionalOnClass
    • @ConditionalOnMissingBean
    • @ConditionalOnBean
    • @ConditionalOnProperty
  • 第6章 外部化配置与属性绑定

    • 章节导读:外部化配置与属性绑定
    • 外部化配置
    • @ConfigurationProperties
    • @Value
    • 配置属性优先级
  • 第7章 Profile 与环境切换

    • 章节导读:Profile与环境切换
    • @Profile
    • 多环境配置文件
  • 第8章 内嵌服务器与部署

    • 章节导读:内嵌服务器与部署
    • 内嵌服务器
    • Fat Jar
  • 第9章 Actuator 与监控

    • 章节导读:Actuator与监控
    • Actuator Health
    • Actuator Info
    • Actuator Metrics
    • 自定义Endpoint
    • 自定义HealthIndicator
  • 第10章 开发工具与最佳实践

    • 章节导读:开发工具与最佳实践
    • Banner自定义
    • 热部署

配置属性优先级

一句话定位:Spring Boot 的 17 级属性源优先级 是一套从高到低的配置覆盖规则,确保命令行参数、环境变量等高优先级来源能够覆盖打包在 JAR 内的默认配置,实现"同一份代码、不同环境运行"。


定义与作用

配置属性优先级 规定了当多个配置来源同时存在同名属性时,哪个来源的值最终生效。这是外部化配置的核心机制——没有优先级规则,"配置与代码分离"就只是一句空话。

Spring Boot 的优先级规则遵循**"越靠近运行时,优先级越高"**的原则:

  • 命令行参数(启动时传入)优先级最高
  • 环境变量次之
  • 打包在 JAR 内的 application.yml 优先级最低

对比传统 SSM 的繁琐过程

维度传统 SSMSpring Boot 优先级机制
配置覆盖无统一规则,靠 XML 加载顺序或手动合并17 级明确优先级,自动覆盖
运维介入需要修改 XML 文件并重新打包只需传命令行参数或环境变量
问题排查不知道哪个配置最终生效通过 /actuator/env 端点可查看实际生效值
默认值代码中硬编码或 XML 中写死低优先级配置源提供默认值,高优先级覆盖

适用位置与常用属性

17 级优先级全景表

优先级属性来源覆盖能力示例
1命令行参数最高--server.port=9090
2SPRING_APPLICATION_JSON(环境变量)极高SPRING_APPLICATION_JSON={"server":{"port":9090}}
3JNDI 属性高java:comp/env/spring.datasource.url
4ServletConfig 初始化参数高web.xml 中的 <init-param>
5ServletContext 初始化参数高web.xml 中的 <context-param>
6System.getProperties()(系统属性)高-Dserver.port=9090
7System.getenv()(环境变量)高SERVER_PORT=9090
8application-{profile}.properties(./config 子目录)中./config/application-dev.yml
9application-{profile}.properties(当前目录)中./application-dev.yml
10application.properties(./config 子目录)中./config/application.yml
11application.properties(当前目录)中./application.yml
12application-{profile}.properties(classpath config)低classpath:config/application-dev.yml
13application-{profile}.properties(classpath)低classpath:application-dev.yml
14application.properties(classpath config)低classpath:config/application.yml
15application.properties(classpath)最低classpath:application.yml
16@PropertySource 注解导入兜底@PropertySource("classpath:custom.yml")
17SpringApplication.setDefaultProperties()最后兜底代码中硬编码默认值

注意:Spring Boot 2.4 之前 spring.profiles 的优先级略有不同,本教程基于 Spring Boot 2.7.18。


核心原理

17 级优先级层次图

图解释:上图从上到下展示优先级从高到低。运行时层(命令行、环境变量、系统属性)永远覆盖配置文件层;Profile 层的配置文件(application-dev.yml)覆盖同位置的非 Profile 配置文件;默认层(@PropertySource、代码默认值)只在所有上层都未配置时生效。

配置合并与覆盖机制

核心原理:ConfigurableEnvironment 内部维护一个有序的 PropertySource 列表,查询属性时按顺序遍历,第一个匹配的值立即返回。这意味着高优先级的来源永远覆盖低优先级的同名属性。


完整示例

场景说明

飞翔科技的学生成绩管理系统需要同时满足以下场景:

  • 开发环境:端口 8080,H2 内存数据库,调试模式开启
  • 测试环境:端口 8081,MySQL 测试库,调试模式关闭
  • 生产环境:端口 80,MySQL 生产库,调试模式关闭

架构师白歌要求:同一份 JAR 包通过外部配置覆盖内部默认值,不能为每个环境单独打包。

后端开发小崔决定在 application.yml(classpath)中放默认值,在 JAR 外部 config/application.yml 放测试环境覆盖值,在启动命令行中传入生产环境参数。

操作前:所有环境共用一份配置,经常搞错

# 操作前:只有一个 application.yml,没有区分环境
server:
  port: 8080

spring:
  datasource:
    url: jdbc:h2:mem:student_db
    driver-class-name: org.h2.Driver

feixiang:
  student:
    debug-mode: true

痛点:

  • 小崔把测试环境的配置误提交到代码仓库,导致生产环境用了测试数据库
  • 每次切换环境都要手动改 application.yml 重新打包
  • 运维无法在不改代码的情况下调整配置

使用优先级机制的完整代码

步骤1:JAR 内部默认配置(classpath:application.yml)—— 优先级15

# src/main/resources/application.yml(dev 默认值)
server:
  port: 8080

spring:
  application:
    name: student-system
  datasource:
    url: jdbc:h2:mem:student_db;DB_CLOSE_DELAY=-1
    driver-class-name: org.h2.Driver
    username: sa
    password:

feixiang:
  student:
    system-name: "飞翔科技学生成绩管理系统"
    debug-mode: true
    max-upload-size: 10MB

步骤2:JAR 外部覆盖配置(./config/application.yml)—— 优先级10

在部署目录创建 config/application.yml:

# ./config/application.yml(测试环境覆盖)
server:
  port: 8081

spring:
  datasource:
    url: jdbc:mysql://test-db:3306/student_db?useSSL=false
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: test_user
    password: test_pass

feixiang:
  student:
    debug-mode: false

步骤3:命令行参数最终覆盖(启动时传入)—— 优先级1

# 生产环境启动,命令行参数覆盖一切
java -jar student-system.jar \
  --server.port=80 \
  --spring.datasource.url=jdbc:mysql://prod-db:3306/student_db?useSSL=true \
  --spring.datasource.username=prod_user \
  --spring.datasource.password=prod_secure_pass \
  --feixiang.student.debug-mode=false

步骤4:读取配置的服务类

package com.feixiang.student.service;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class EnvironmentCheckService {
    
    @Value("${server.port}")
    private int serverPort;
    
    @Value("${spring.datasource.url}")
    private String datasourceUrl;
    
    @Value("${feixiang.student.debug-mode}")
    private boolean debugMode;
    
    public void printEffectiveConfig() {
        System.out.println("=== 当前生效配置 ===");
        System.out.println("端口: " + serverPort);
        System.out.println("数据库: " + datasourceUrl);
        System.out.println("调试模式: " + debugMode);
    }
}

操作后运行结果及分析

场景A:直接启动(无外部配置,仅 classpath 默认值)

=== 当前生效配置 ===
端口: 8080
数据库: jdbc:h2:mem:student_db;DB_CLOSE_DELAY=-1
调试模式: true

场景B:在 JAR 外部 config/application.yml 存在时启动

=== 当前生效配置 ===
端口: 8081                         ← 被 ./config/application.yml 覆盖
数据库: jdbc:mysql://test-db:3306/student_db?useSSL=false  ← 被覆盖
调试模式: false                     ← 被覆盖

场景C:命令行参数启动(生产环境)

=== 当前生效配置 ===
端口: 80                            ← 被 --server.port=80 覆盖
数据库: jdbc:mysql://prod-db:3306/student_db?useSSL=true  ← 被覆盖
调试模式: false                     ← 被覆盖

分析:

  1. 优先级10覆盖15:./config/application.yml 的优先级(10)高于 classpath:application.yml(15),所以测试环境配置生效
  2. 优先级1覆盖一切:命令行参数 --server.port=80 优先级(1)高于所有配置文件,所以生产环境端口被覆盖为 80
  3. 未覆盖的配置保留:如果 ./config/application.yml 中未定义 feixiang.student.system-name,则使用 classpath:application.yml 中的默认值 "飞翔科技学生成绩管理系统"
  4. 运维零代码介入:运维人员只需在服务器上放置配置文件或修改启动脚本,无需改代码

易错场景与面试考点

易错场景一:误以为配置文件位置不重要

小崔在测试服务器上把 application.yml 放在 JAR 包同级目录,但 JAR 内部的 application.yml 仍然生效:

# 目录结构
/opt/apps/
  ├── student-system.jar
  └── application.yml          # ← 小崔放在这里

后果:Spring Boot 的加载顺序中,JAR 外部的 ./application.yml(优先级11)确实高于 classpath:application.yml(优先级15)。但小崔不知道 config/application.yml(优先级10)比 ./application.yml(优先级11)更高。运维同时放了 config/application.yml,导致小崔的配置被覆盖。

正确做法:明确区分三种外部位置的优先级:./config/ > ./ > classpath。在团队协作中约定统一的配置放置位置,避免多层覆盖导致困惑。

易错场景二:环境变量名写错导致覆盖失败

小崔想在 Linux 服务器上通过环境变量覆盖数据库 URL:

# 错误示范:环境变量命名不符合松散绑定
export spring.datasource.url=jdbc:mysql://prod-db:3306/student_db

后果:Shell 环境变量名中包含点号(.)在某些环境中不合法,且 Spring Boot 的 SystemEnvironmentPropertySource 期望使用下划线替代点号。该环境变量未被 Spring Boot 识别,应用仍使用 application.yml 中的默认值。

正确做法:环境变量使用全大写,点号替换为下划线:

export SPRING_DATASOURCE_URL=jdbc:mysql://prod-db:3306/student_db
export FEIXIANG_STUDENT_DEBUG_MODE=false

面试考点

Q:Spring Boot 的配置属性优先级是什么?列举前5级。

从高到低:1. 命令行参数 --key=value;2. SPRING_APPLICATION_JSON 环境变量;3. JNDI 属性;4. ServletConfig 初始化参数;5. ServletContext 初始化参数;6. 系统属性 -Dkey=value;7. 环境变量。核心原则是越靠近运行时,优先级越高。

Q:为什么命令行参数优先级最高?

命令行参数是启动时最后传入的,由运维或部署脚本控制。最高优先级确保运维可以在不修改代码、不修改配置文件的情况下,临时调整应用行为(如紧急切换端口或关闭某个功能)。

Q:如何查看当前生效的配置值?

引入 spring-boot-starter-actuator,访问 /actuator/env 端点,可以看到所有 PropertySource 以及每个属性的实际生效值和来源。此外,/actuator/configprops 可以看到所有 @ConfigurationProperties 的绑定结果。

Q:多个配置文件同时存在时,Spring Boot 如何合并?

不是"合并内容",而是按优先级覆盖同名属性。每个 PropertySource 是一个独立的键值集合,查询时按优先级从高到低遍历,第一个匹配的值即返回。不同属性可以共存(如 server.port 在命令行参数中,spring.datasource.url 在 application.yml 中)。

Q:application.yml 和 application.properties 同时存在时,哪个生效?

如果同名文件(同路径)同时存在 .yml 和 .properties,Spring Boot 2.4 之前的版本是 .properties 覆盖 .yml;2.4 之后改为 .yml 覆盖 .properties。但最佳实践是只使用一种格式,避免混淆。本教程统一使用 YAML。

上一页
@Value