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

    • 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 的配置与代码分离机制,允许通过 application.yml、环境变量、命令行参数等外部来源覆盖应用行为,实现同一份代码在不同环境运行不同配置。


定义与作用

外部化配置(Externalized Configuration) 是 Spring Boot 的核心理念之一,它将应用的配置参数(如数据库连接、端口号、功能开关)从 Java 代码中抽离,放到外部文件或环境中。这样做的好处是:打包后的 JAR 包不需要修改即可在 dev、test、prod 等不同环境运行。

对比传统 SSM 的繁琐过程

维度传统 SSM(Spring + Spring MVC + MyBatis)Spring Boot 外部化配置
配置位置分散在 applicationContext.xml、<property> 标签中统一在 application.yml 或外部来源
环境切换需要修改 XML 文件重新打包只需修改外部配置或启动参数
部署方式修改配置 → 重新编译 → 打 WAR → 部署到 Tomcatjava -jar 启动,配置通过外部传入
优先级无统一优先级,靠 XML 加载顺序17级明确优先级,高覆盖低
运维友好度差,配置藏在代码包内部优,配置与代码完全分离

适用位置与常用属性

配置来源一览

Spring Boot 支持从以下位置读取配置(按优先级从高到低排列):

来源说明示例
命令行参数启动时传入的 --key=value--server.port=9090
SPRING_APPLICATION_JSON环境变量中的 JSON 字符串{"server":{"port":9090}}
JNDI 属性Servlet 容器中的 JNDI 绑定java:comp/env/spring.datasource.url
ServletConfig 初始化参数web.xml 中的 <init-param>传统 WAR 部署场景
ServletContext 初始化参数web.xml 中的 <context-param>传统 WAR 部署场景
系统属性(System Properties)JVM 启动参数 -D-Dserver.port=9090
环境变量(Environment Variables)操作系统环境变量SERVER_PORT=9090
application.yml / application.properties打包在 JAR 内或外部的配置文件application.yml
@PropertySource自定义配置文件路径@PropertySource("classpath:custom.yml")
SpringApplication.setDefaultProperties()代码中硬编码的默认值setDefaultProperties(...)
spring-boot-devtools 全局设置开发者工具全局配置文件~/.spring-boot-devtools.properties

统一使用 application.yml

本教程统一采用 YAML 格式,所有示例围绕飞翔科技学生成绩管理系统:

# application.yml(dev 环境示例)
server:
  port: 8080

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

feixiang:
  student:
    system-name: "飞翔科技学生成绩管理系统"
    version: "2.7.18"
    features:
      - cache
      - log

核心原理

17级配置源优先级全景

图解释:Spring Boot 启动时,按从上到下的顺序将所有配置源加载到 Environment 中。高优先级的属性值会覆盖低优先级的同名属性。例如命令行参数 --server.port=9090 会覆盖 application.yml 中的 server.port: 8080。

配置读取与注入流程

核心组件:

  • ConfigurableEnvironment:持有所有 PropertySource,按优先级排序
  • ConfigurationPropertiesBinder:负责将外部属性绑定到 Java 对象
  • ConversionService:将字符串值转换为目标类型(如 "5000" → int)

完整示例

场景说明

飞翔科技学生成绩管理系统需要支持三种环境:

  • dev:本地开发,使用 H2 内存数据库,端口 8080
  • test:测试环境,使用 MySQL 测试库,端口 8081
  • prod:生产环境,使用 MySQL 生产库,端口 80

架构师白歌要求小崔确保同一份 JAR 包可以通过外部配置在不同环境运行,不能为了换环境重新打包。

操作前:配置硬编码在代码中

// 操作前:配置硬编码,环境切换必须改代码重新编译
package com.feixiang.student.config;

public class HardcodedConfig {
    // 数据库配置写死在代码里
    private String dbUrl = "jdbc:h2:mem:student_db";
    private String dbDriver = "org.h2.Driver";
    private String dbUsername = "sa";
    private String dbPassword = "";
    
    // 系统信息也写死
    private String systemName = "飞翔科技学生成绩管理系统";
    private int serverPort = 8080;
    
    // 如果要切换到 MySQL 生产环境,必须修改源代码、重新编译、重新打包
    // 这是严重的设计缺陷
}

痛点:

  • 每切换一次环境就要改代码、重新编译、重新打包
  • 数据库密码等敏感信息暴露在代码仓库中
  • 无法做到"一次构建,到处运行"

使用外部化配置的完整代码

步骤1:创建统一的 application.yml

# src/main/resources/application.yml
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: "飞翔科技学生成绩管理系统"
    version: "2.7.18"
    max-upload-size: 10MB
    features:
      - cache
      - log
      - monitor

步骤2:在代码中通过 @Value 读取配置

package com.feixiang.student.config;

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

@Component
public class SystemConfig {
    
    @Value("${feixiang.student.system-name}")
    private String systemName;
    
    @Value("${feixiang.student.version}")
    private String version;
    
    @Value("${server.port:8080}")
    private int serverPort;
    
    @Value("${spring.datasource.url}")
    private String databaseUrl;
    
    public void printConfig() {
        System.out.println("系统名称: " + systemName);
        System.out.println("版本: " + version);
        System.out.println("端口: " + serverPort);
        System.out.println("数据库: " + databaseUrl);
    }
}

步骤3:通过外部配置覆盖(不修改代码)

切换到测试环境时,只需在启动时传入外部配置:

# 方式1:命令行参数覆盖(最高优先级)
java -jar student-system.jar \
  --server.port=8081 \
  --spring.datasource.url=jdbc:mysql://test-db:3306/student_db \
  --spring.datasource.username=test_user \
  --spring.datasource.password=test_pass

# 方式2:环境变量覆盖(Linux/Mac)
export SERVER_PORT=8081
export SPRING_DATASOURCE_URL=jdbc:mysql://test-db:3306/student_db
java -jar student-system.jar

# 方式3:外部配置文件(./config/application.yml 优先级高于 JAR 内部)
# 在 JAR 同级目录创建 config/application.yml,写入测试环境配置

操作后运行结果及分析

启动时控制台输出:

系统名称: 飞翔科技学生成绩管理系统
版本: 2.7.18
端口: 8081
数据库: jdbc:mysql://test-db:3306/student_db

分析:

  1. 配置与代码分离:SystemConfig 类中只声明了配置项的名称,具体值来自外部
  2. 环境切换零代码改动:从 dev 切换到 test 时,application.yml 中的默认值被命令行参数覆盖
  3. 优先级生效:--server.port=8081(命令行,优先级1)覆盖了 application.yml 中的 server.port: 8080(优先级14)
  4. 敏感信息不外泄:生产数据库密码通过环境变量传入,不写入代码仓库

易错场景与面试考点

易错场景一:YAML 缩进错误导致配置不生效

小崔在写 application.yml 时,不小心把 feixiang.student 写成了同层级:

# 错误示范:缩进错误
feixiang:
student:    # ← 缩进错误!应该再缩进两格
  system-name: "飞翔科技学生成绩管理系统"

后果:YAML 解析器认为 student 是 feixiang 的同级节点,而不是子节点。@Value("${feixiang.student.system-name}") 读取不到值,抛出 IllegalArgumentException: Could not resolve placeholder 'feixiang.student.system-name'。

正确做法:YAML 对缩进极其敏感,必须使用2个空格(不能是 Tab),严格保持层级关系:

feixiang:
  student:
    system-name: "飞翔科技学生成绩管理系统"

易错场景二:环境变量命名不规范导致绑定失败

小崔在 Linux 服务器上设置了环境变量:

# 错误示范:环境变量命名不符合松散绑定规则
export feixiang.student.max-upload-size=20MB

后果:环境变量名中包含点号(.)在大多数 Shell 中不合法,且 Spring Boot 的环境变量松散绑定规则要求将点号替换为下划线并转为大写。即使 Shell 允许,Spring Boot 的 SystemEnvironmentPropertySource 也无法正确解析。

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

export FEIXIANG_STUDENT_MAX_UPLOAD_SIZE=20MB
export SPRING_DATASOURCE_URL=jdbc:mysql://prod-db:3306/student_db

面试考点

Q:Spring Boot 外部化配置的核心设计思想是什么?

约定优于配置 + 配置与代码分离。Spring Boot 通过 application.yml 提供合理的默认值,同时允许通过外部来源(命令行、环境变量等)覆盖,实现同一份代码在不同环境运行。

Q:Spring Boot 的配置文件有哪些加载位置?优先级如何?

从低到高:classpath 根目录 → classpath 的 config/ 子目录 → JAR 所在目录 → JAR 同级 config/ 子目录。外部目录的配置文件优先级高于 JAR 内部的文件。完整优先级共17级,命令行参数最高,代码中的默认值最低。

Q:YAML 和 Properties 格式如何选择?

YAML 适合层级化的复杂配置(如嵌套对象、List),可读性更好;Properties 适合扁平化配置。Spring Boot 官方推荐 YAML,本教程统一使用 application.yml。

Q:环境变量 SERVER_PORT 为什么能映射到 server.port?

Spring Boot 的 RelaxedDataBinder 支持松散绑定。环境变量 SERVER_PORT 经过转换(去掉下划线、转小写)后可以匹配到 server.port。同理,FEIXIANG_STUDENT_MAX_UPLOAD_SIZE 可以匹配到 feixiang.student.max-upload-size。

上一页
章节导读:外部化配置与属性绑定
下一页
@ConfigurationProperties