Spring Boot Profile 与外部化配置详解

文章目录

一、Profile 功能

Profile 是 Spring Boot 提供的环境隔离功能,可实现不同环境下配置的灵活切换。

1. 环境配置文件规则

  • Spring Boot 支持按环境定义配置文件,命名格式为 application-{环境名}.yaml/.properties/.xml
  • 默认配置文件 application.yaml/.properties 适用于所有环境,作为基础配置

2. 激活指定环境

(1)配置文件激活

在默认配置文件中通过 spring.profiles.active 属性指定激活的环境,多个环境用英文逗号分隔

yaml 复制代码
# application.yaml
spring:
  profiles:
    active: dev,test # 同时激活 dev 和 test 环境
(2)命令行激活(优先级最高)

启动 Jar 包时通过命令行参数指定环境,同时可直接覆盖配置文件中的属性

bash 复制代码
java -jar xxx.jar --spring.profiles.active=prod --person.name=haha

3. 配置生效规则

  • 默认配置与激活的环境配置同时生效
  • 同名配置项,环境配置优先于默认配置,后激活的环境优先于先激活的环境

4. @Profile 条件配置

通过 @Profile 注解可实现类或方法的条件加载,只有当指定环境被激活时,该类才会被 Spring 容器加载

java 复制代码
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration(proxyBeanMethods = false)
@Profile("production") // 仅在 production 环境激活时生效
public class ProductionConfiguration {
    // 业务逻辑
}
  • 注解值可以是环境名环境组名
  • 若通过 @EnableConfigurationProperties 注册配置类,@Profile 需标注在对应的 @Configuration 类上

5. Profile 分组

当环境配置过于细粒度时,可通过分组功能将多个相关环境整合为一个逻辑组,简化激活操作

(1)定义分组

在默认配置文件中配置分组关系

yaml 复制代码
# application.yaml
spring:
  profiles:
    group:
      production: [proddb, prodmq] # production 组包含 proddb 和 prodmq 两个环境
(2)激活分组

激活分组时,组内所有环境会被同时激活

bash 复制代码
java -jar xxx.jar --spring.profiles.active=production

6. IDEA 配置多环境启动

若 IDEA 中没有 VM 选项框,可通过以下步骤配置:

  1. 点击启动配置下拉框 → Edit Configurations
  2. Configuration 标签页中,勾选 Allow multiple instances 允许启动多实例
  3. Program arguments 中输入命令行参数,例如 --spring.profiles.active=dev

二、外部化配置

外部化配置指将配置与代码解耦,通过多种外部配置源实现不同环境的适配,核心文档参考:Spring Boot 外部化配置官方文档

1. 外部配置源

Spring Boot 支持多种配置源,常用类型如下:

(1)Properties 文件

采用键值对格式,层级用英文点号分隔

properties 复制代码
# application.properties
person.age=23
server.port=8083
(2)YAML 文件

采用层级缩进格式,更适合复杂配置结构

yaml 复制代码
# application.yaml
server:
  port: 8082
person:
  age: 23
(3)命令行参数

启动时通过 --参数名=值 传递,优先级最高,示例见「激活指定环境」章节

(4)其他配置源

还支持 环境变量、Java 系统属性、JNDI 属性、SPRING_APPLICATION_JSON 等配置源

2. 配置优先级

Spring Boot 按以下顺序加载配置(后面的配置源会覆盖前面的同名配置

  1. 默认属性(SpringApplication.setDefaultProperties 设置)
  2. @PropertySource 注解加载的配置(注意:无法加载 logging.*spring.main.* 等早期读取的属性)
  3. 配置数据文件(application.properties/yaml 及其环境变体)
  4. RandomValuePropertySource(生成 random.* 随机属性)
  5. 操作系统环境变量
  6. Java 系统属性(System.getProperties()
  7. JNDI 属性(java:comp/env
  8. ServletContext 初始化参数
  9. ServletConfig 初始化参数
  10. SPRING_APPLICATION_JSON(嵌入在环境变量或系统属性中的 JSON 字符串)
  11. 命令行参数
  12. 测试相关属性(@SpringBootTestproperties 属性、@TestPropertySource 注解)
  13. Devtools 全局配置($HOME/.config/spring-boot 目录,仅 Devtools 激活时生效)

3. 配置文件查找位置

Spring Boot 启动时,会自动从以下位置加载 application.properties/yaml 文件,位置越靠后优先级越高

  1. 类路径内部
    • 类路径根目录(resources 目录)
    • 类路径 /config 子目录
  2. 当前目录(相对于 Jar 包所在目录)
    • Jar 包所在目录
    • Jar 包所在目录的 /config 子目录
    • /config 子目录的直接子目录

4. 配置文件加载顺序

  1. 当前 Jar 包内部的 application.properties/yaml
  2. 当前 Jar 包内部的 application-{profile}.properties/yaml
  3. 外部 Jar 包的 application.properties/yaml
  4. 外部 Jar 包的 application-{profile}.properties/yaml

核心规则:指定环境配置优先于默认配置,外部配置优先于内部配置,后加载的配置覆盖先加载的同名配置

三、配置文件类型详解

1. Properties 文件

  • 语法:key=value,层级用 . 分隔
  • 编码:默认 ISO-8859-1,可通过 spring.config.encoding 指定编码格式
  • 特点:语法简单,兼容性好,但复杂层级配置可读性较差

2. YAML 文件

YAML 是一种以数据为中心的配置格式,相比 Properties 更适合复杂层级结构

(1)基本语法
  • 键值对格式:key: value冒号后必须加空格
  • 大小写敏感
  • 空格缩进表示层级关系,不允许使用 Tab 键
  • 缩进空格数不固定,相同层级元素左对齐即可
  • 注释:# 开头的行表示注释
  • 字符串:默认无需引号;双引号会转义特殊字符(如 \n 会被解析为换行符);单引号不转义特殊字符
(2)支持的数据类型
  1. 字面量:单个、不可再分的值(字符串、数字、布尔值、日期、null)

    yaml 复制代码
    str: hello world
    num: 123
    bool: true
    date: 2024-01-01
    nullVal: ~ # 表示 null
  2. 对象:键值对的集合,支持两种写法

    yaml 复制代码
    # 多行写法
    person:
      name: zhangsan
      age: 20
    # 行内写法
    person: {name: zhangsan, age: 20}
  3. 数组:一组有序的值,支持两种写法

    yaml 复制代码
    # 多行写法
    list:
      - apple
      - banana
      - orange
    # 行内写法
    list: [apple, banana, orange]
(3)属性引用

可通过 ${key} 引用已定义的属性,支持默认值 ${key:默认值}

yaml 复制代码
spring:
  datasource:
    name: mydb
url: jdbc:mysql://localhost:3306/${spring.datasource.name:defaultdb}
(4)注意事项
  • YAML 对数字格式敏感,以 0 开头的数字会被解析为八进制,以 0x 开头的会被解析为十六进制

  • 若需将数字作为字符串处理,必须用引号包裹

    yaml 复制代码
    # 正确写法:作为字符串
    password: "012345"
    # 错误写法:会被解析为八进制数字 52428
    password: 012345

3. 关闭命令行参数传递

若不需要通过命令行传递参数,可修改 Spring Boot 入口类,启动时不传入 args 参数

java 复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        // 不传入 args 参数,关闭命令行参数解析
        SpringApplication.run(MyApplication.class);
    }
}

四、自定义配置类绑定与提示

1. 自定义配置类绑定

通过 @ConfigurationProperties 注解可将配置文件中的属性绑定到自定义 Java 类,实现类型安全的配置管理

java 复制代码
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "person") // 绑定前缀为 person 的属性
public class PersonProperties {
    private String name;
    private Integer age;

    // Getter 和 Setter 方法
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
}

2. 开启配置提示

自定义配置类默认没有 IDE 自动提示,需导入配置处理器依赖解决

(1)添加依赖
xml 复制代码
<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>
(2)排除打包依赖

为避免配置处理器被打包到最终的 Jar 包中,需在 Maven 插件中配置排除

xml 复制代码
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <excludes>
                    <exclude>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-configuration-processor</artifactId>
                    </exclude>
                </excludes>
            </configuration>
        </plugin>
    </plugins>
</build>

四、面试题 Spring Boot 读取配置文件的 5 种常用方式

Spring Boot 读取配置文件的核心是利用其外部化配置机制,支持多种灵活方式,覆盖简单属性、批量绑定、复杂场景等需求,以下是具体实现方法和注意事项:

1、@Value 注解:直接读取单个属性(最简单)

适用场景

快速读取单个简单配置项(如端口、用户名、常量等),无需批量绑定。

实现步骤
  1. 配置文件(以 YAML 为例):
yaml 复制代码
# application.yaml
server:
  port: 8080
person:
  name: 张三
  age: 25
  address: 北京
  1. 代码中注入:
java 复制代码
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class MyComponent {
    // 直接读取单个属性,支持默认值(找不到配置时用默认值)
    @Value("${server.port}")
    private Integer serverPort;

    @Value("${person.name}")
    private String personName;

    // 配置不存在时,使用默认值 "上海"
    @Value("${person.address:上海}")
    private String personAddress;

    // 测试方法
    public void printConfig() {
        System.out.println("端口:" + serverPort);
        System.out.println("姓名:" + personName);
        System.out.println("地址:" + personAddress);
    }
}
注意事项
  • 语法格式:@Value("${配置键:默认值}"),默认值可选(冒号后为默认值);
  • 支持松散绑定吗?有限支持!仅当配置键用短横线命名 (如 person.user-name),注解中用 @Value("${person.user-name}") 才能匹配,驼峰命名(person.userName)不兼容;
  • 不支持复杂类型(如对象、数组),仅适用于字符串、数字、布尔值等字面量。

2、@ConfigurationProperties:批量绑定配置(推荐)

适用场景

读取多个相关配置项(如一个对象的多个属性),实现类型安全绑定,替代多个 @Value 注解。

实现步骤
  1. 配置文件(YAML):
yaml 复制代码
# application.yaml
person:
  name: 李四
  age: 30
  address: 广州
  hobbies: [篮球, 读书] # 数组
  contact:
    phone: 13800138000
    email: lisi@example.com # 嵌套对象
  1. 定义配置类(批量绑定):
java 复制代码
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;

// 绑定前缀为 "person" 的所有配置
@Component
@ConfigurationProperties(prefix = "person")
public class PersonProperties {
    // 字段名与配置键一致(支持松散绑定:hobbies = person.hobbies = person.hob-bies)
    private String name;
    private Integer age;
    private String address;
    private List<String> hobbies; // 数组/集合类型
    private Contact contact; // 嵌套对象类型

    // 必须提供 Getter 和 Setter 方法(否则绑定失败)
    // Getter + Setter 省略...

    // 嵌套对象类
    public static class Contact {
        private String phone;
        private String email;

        // Getter + Setter 省略...
    }
}
  1. 使用配置类:
java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class MyService {
    @Autowired
    private PersonProperties personProperties;

    public void printPersonConfig() {
        System.out.println("姓名:" + personProperties.getName());
        System.out.println("爱好:" + personProperties.getHobbies());
        System.out.println("手机号:" + personProperties.getContact().getPhone());
    }
}
核心优势
  • 支持松散绑定:配置键可写 person.user-name(短横线)、person.userName(驼峰)、person.user_name(下划线),字段名用驼峰(userName)即可匹配;
  • 支持复杂类型:数组、集合、嵌套对象等自动绑定,无需手动转换;
  • 类型安全:字段类型与配置自动匹配(如配置 age: 30 自动转为 Integer);
  • IDE 提示:导入 spring-boot-configuration-processor 依赖后,配置文件会有语法提示(避免写错键名)。
注意事项
  • 必须加 @Component(或通过 @EnableConfigurationProperties 激活),否则 Spring 无法扫描到;
  • 字段必须提供 Getter 和 Setter 方法(绑定依赖 JavaBean 规范);
  • 前缀 prefix 必须用短横线命名 (如 person.contact,不能写 personContact)。

3、Environment 接口:动态获取配置(灵活)

适用场景

需要动态获取配置(如根据条件读取不同键的配置),或不确定配置键的固定名称。

实现步骤
  1. 配置文件(Properties 为例):
properties 复制代码
# application.properties
app.id=APP_001
app.mode=prod
app.version=2.0.0
  1. 注入 Environment 接口:
java 复制代码
import org.springframework.core.env.Environment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class EnvConfigReader {
    @Autowired
    private Environment env;

    public void readConfig() {
        // 读取配置,参数为配置键
        String appId = env.getProperty("app.id");
        String appMode = env.getProperty("app.mode");
        
        // 读取并指定类型(自动转换为 Integer)
        Integer appVersion = env.getProperty("app.version", Integer.class);
        
        // 读取不存在的配置,返回默认值 "1.0.0"
        String fallbackVersion = env.getProperty("app.backup.version", "1.0.0");

        System.out.println("应用ID:" + appId);
        System.out.println("运行模式:" + appMode);
        System.out.println("版本号:" + appVersion);
        System.out.println("备用版本:" + fallbackVersion);
    }
}
核心特点
  • 支持动态键:可通过变量拼接配置键(如 env.getProperty("app." + type));
  • 支持类型转换:getProperty(String key, Class<T> type) 自动转换为指定类型;
  • 支持默认值:getProperty(String key, String defaultValue)getProperty(String key, Class<T> type, T defaultValue)
  • 支持松散绑定吗?不支持!配置键必须与文件中完全一致(如文件中是 app.user-name,就不能用 app.userName 读取)。

4、@PropertySource:加载自定义配置文件(非默认)

适用场景

默认配置文件(application.yaml/application.properties)外,还需要加载自定义配置文件(如 config/user.propertiesconfig/db.yaml)。

实现步骤(加载自定义 Properties 文件)
  1. 自定义配置文件(resources/config/user.properties):
properties 复制代码
# resources/config/user.properties
user.id=1001
user.name=王五
user.age=28
  1. 加载并绑定:
java 复制代码
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

// 加载自定义配置文件(classpath 表示 resources 目录)
@Component
@PropertySource(value = "classpath:config/user.properties", encoding = "UTF-8")
@ConfigurationProperties(prefix = "user") // 绑定前缀 "user"
public class UserProperties {
    private Integer id;
    private String name;
    private Integer age;

    // Getter + Setter 省略...
}
注意事项
  • 编码问题:若配置文件有中文,需指定 encoding = "UTF-8"(默认 ISO-8859-1,会乱码);
  • 支持 YAML 文件吗?默认不支持!@PropertySource 仅支持 .properties 文件,若要加载自定义 YAML 文件,需自定义 PropertySourceFactory(后续补充);
  • 优先级:自定义文件的配置优先级低于默认配置文件(application.yaml),同名配置会被默认文件覆盖。
扩展:加载自定义 YAML 文件

需自定义工厂类,让 @PropertySource 支持 YAML:

  1. 自定义工厂类:
java 复制代码
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import java.io.IOException;

public class YamlPropertySourceFactory implements PropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        // 用 Spring 提供的 YamlPropertySourceLoader 加载 YAML 文件
        return new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource())
                .get(0);
    }
}
  1. 加载自定义 YAML 文件(resources/config/db.yaml):
yaml 复制代码
# resources/config/db.yaml
db:
  url: jdbc:mysql://localhost:3306/mydb
  username: root
  password: 123456
  1. 绑定配置:
java 复制代码
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@PropertySource(value = "classpath:config/db.yaml", factory = YamlPropertySourceFactory.class, encoding = "UTF-8")
@ConfigurationProperties(prefix = "db")
public class DbProperties {
    private String url;
    private String username;
    private String password;

    // Getter + Setter 省略...
}

5、配置树(Configuration Tree):读取目录式配置(云环境常用)

适用场景

Kubernetes 挂载 ConfigMaps/Secrets、Docker Secrets 等场景,配置以"目录-文件"形式存储(文件名=配置键,文件内容=配置值)。

实现步骤

  1. 假设 Kubernetes 挂载配置到 /etc/config/myapp/ 目录,目录结构:

    /etc/config/myapp/
    ├── username.txt (内容:admin)
    ├── password.txt (内容:123456)
    └── db/
    ├── url.txt (内容:jdbc:mysql://localhost:3306/mydb)
    └── driver.txt (内容:com.mysql.cj.jdbc.Driver)

  2. 配置文件中导入配置树:

yaml 复制代码
# application.yaml
spring:
  config:
    import: optional:configtree:/etc/config/myapp/ # 导入目录式配置
  1. 绑定配置类:
java 复制代码
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "myapp")
public class MyappConfig {
    private String username; // 对应 /etc/config/myapp/username.txt
    private String password; // 对应 /etc/config/myapp/password.txt
    private Db db; // 对应 /etc/config/myapp/db/ 目录

    // Getter + Setter 省略...

    public static class Db {
        private String url; // 对应 /etc/config/myapp/db/url.txt
        private String driver; // 对应 /etc/config/myapp/db/driver.txt

        // Getter + Setter 省略...
    }
}
核心特点
  • 导入语法:configtree:目录路径optional: 前缀表示目录不存在也不报错;
  • 配置键规则:目录名+文件名组成配置键(如 db.url = db 目录 + url 文件);
  • 适用场景:云环境敏感配置(如密码),避免暴露为环境变量,通过文件挂载安全读取。

6、常见问题与注意事项

  1. 配置键写错了怎么办?

    • @Value 会直接抛出 IllegalArgumentException(找不到配置),除非设置了默认值;
    • @ConfigurationProperties 不会报错,字段值为 null(可通过 @Validated 注解校验,强制必填)。
  2. 中文乱码怎么解决?

    • Properties 文件:指定编码为 UTF-8(@PropertySource(encoding = "UTF-8"));
    • YAML 文件:默认 UTF-8 编码,无需额外配置(确保文件本身是 UTF-8 格式)。
  3. 不同配置源的优先级

    命令行参数 > 环境变量 > 外部配置文件 > 内部配置文件 > 自定义配置文件(@PropertySource),后加载的配置会覆盖先加载的同名配置。


文章结束,喜欢就给个一键三连吧,你的肯定是我最大的动力,点赞上一千我就是脑瘫也出下章

相关推荐
云烟成雨TD15 小时前
Spring AI Alibaba 1.x 系列【6】ReactAgent 同步执行 & 流式执行
java·人工智能·spring
Java成神之路-15 小时前
SpringMVC 响应实战指南:页面、文本、JSON 返回全流程(Spring系列13)
java·spring·json
砍材农夫16 小时前
spring-ai 第六模型介绍-聊天模型
java·人工智能·spring
云烟成雨TD16 小时前
Spring AI Alibaba 1.x 系列【5】ReactAgent 构建器深度源码解析
java·人工智能·spring
Flittly17 小时前
【SpringAIAlibaba新手村系列】(15)MCP Client 调用本地服务
java·笔记·spring·ai·springboot
Flittly17 小时前
【SpringAIAlibaba新手村系列】(14)MCP 本地服务与工具集成
java·spring boot·笔记·spring·ai
mfxcyh18 小时前
基于xml、注解、JavaConfig实现spring的ioc
xml·java·spring
Flittly18 小时前
【SpringAIAlibaba新手村系列】(13)Tool Calling 函数工具调用技术
java·spring boot·spring·ai
xdscode18 小时前
Spring 依赖注入方式全景解析
java·后端·spring