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),后加载的配置会覆盖先加载的同名配置。


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

相关推荐
on the way 1232 小时前
day07-Spring循环依赖
后端·spring
csdnfanguyinheng2 小时前
生产级的考试系统
java·springboot·考试
康小庄2 小时前
通过NGINX实现将小程序HTTPS请求转为内部HTTP请求
java·spring boot·nginx·spring·http·小程序
Swift社区2 小时前
Date / LocalDateTime 转换错误,一次踩坑后的完整复盘
java·spring boot·spring
0和1的舞者3 小时前
SpringBoot 接口规范:统一返回、异常处理与拦截器详解
java·spring boot·后端·spring·知识·统一
ZeroToOneDev3 小时前
SpringMvc
java·spring
坚持学习前端日记3 小时前
认证模块文档
java·服务器·前端·数据库·spring
仙俊红12 小时前
spring的IoC(控制反转)面试题
java·后端·spring
阿湯哥12 小时前
AgentScope Java 集成 Spring AI Alibaba Workflow 完整指南
java·人工智能·spring