什么是Profiles

介绍

随着软件开发变得越来越复杂,应用程序往往需要在不同的环境中运行,比如开发、测试、生产等。每个环境可能对配置有不同的需求。手动管理这些差异化的配置不仅容易出错,而且效率低下。为了解决这个问题,Spring框架引入了Profiles的概念,它允许开发者根据不同的环境来激活特定的配置设置。

Spring Profiles 是Spring框架提供的一种机制,用来解决多环境配置问题。通过定义不同的profiles,可以在同一套代码库中针对不同的运行时环境(如开发、测试、生产)指定不同的配置属性,这样就提供了一种方式允许我们指定在特定环境下只加载对应的程序配置,每一种环境配置对应一个 Profile,只有当前 Profile 处于激活状态时,才会将该 Profile 所对应的配置和 Bean 加载到 Spring 程序中。这样做的好处是能够保持代码的一致性,同时又能灵活地适应各种环境的变化。

Spring Profiles 就是针对应用程序,不同环境需要不同配置加载的一种解决方案。

Profile 的概念其实很早在 Spring Framework 就有了,在 Spring Framework 3.1 版本引入了注解 @ProfileEnvironment 环境配置的抽象,只是在 Spring Boot 框架里再进一步将 Profiles 功能进行扩展,使它也成为了 Spring Boot 特性之一,为此单独在 官方文档 25. Profiles 一节里介绍,文档里把 Spring Boot Profiles 也叫做 Spring Profiles。

当然 Spring 允许多个 Profile 处于激活状态,在需要激活环境对应配置时,指定多个 Profile。

在实际开发中,Profile 可以用来实现以下几种功能:

  • 区分不同的环境,例如开发环境、测试环境和生产环境。
  • 配置不同的数据库连接信息
  • 配置不同的日志级别

使用 Profiles

如何使用

在 Spring Boot 中,Profile 是通过配置文件来实现的。在不同的环境下,可以加载不同的配置文件,从而实现不同的配置逻辑。具体来说,Spring Boot 支持以下几种配置文件:

application.properties
application.yml
application-{profile}.properties
application-{profile}.yml

其中,application.propertiesapplication.yml 是通用的配置文件,它们在所有的环境下都会被加载。而 application-{profile}.propertiesapplication-{profile}.yml 则是根据不同的 Profile 来加载的配置文件。当应用程序启动时,Spring Boot 会根据当前的环境变量来决定加载哪个配置文件。例如,如果当前环境变量为 dev,则会加载 application-dev.propertiesapplication-dev.yml 文件。

我们使用 Profiles 的步骤一般如下:

  • 标识环境:指定哪些组件、配置在哪个环境⽣效
  • 切换环境:这个环境对应的所有组件和配置就应该⽣效

在 Spring 中,Profiles 用于实现 “环境隔离”(比如区分开发、测试、生产环境的配置),核心有两种使用方式:XML 配置@Profile 注解

  • XML 就不细说了,在 XML 配置文件中,通过 <beans profile="环境标识"> 来隔离不同环境的 Bean 定义,注意必须使用 Spring XML Beans Schema 4.0+(对应 Spring 3.1+ 版本),否则 <beans profile> 会报错。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <!-- 开发环境 Bean -->
    <beans profile="dev">
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="url" value="jdbc:mysql://dev.db:3306/test" />
    <!-- 其他开发环境配置 -->
    </bean>
    </beans>

    <!-- 生产环境 Bean -->
    <beans profile="prod">
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="url" value="jdbc:mysql://prod.db:3306/test" />
    <!-- 其他生产环境配置 -->
    </bean>
    </beans>

    此时激活有两种方式

    • 通过 spring.profiles.active 指定环境

      1
      2
      3
      4
      // 代码方式激活
      new AnnotationConfigApplicationContext()
      .getEnvironment()
      .setActiveProfiles("dev");
    • 或通过 JVM 参数:

      1
      -Dspring.profiles.active=prod
  • @Profile 是更灵活的 注解式 环境隔离方案,支持标注在 方法 上,任何@Component@Configuration或者@ConfigurationProperties,决定 Bean 是否在特定环境下生效。容器中的组件都可以被 @Profile 标记

    • 标注在 @Configuration 类上

      示例,区分开发和生产环境的配置类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      // 开发环境配置类(可以写多个,当 dev 和 test 环境激活时生效)
      @Configuration
      @Profile({"dev", "test"})
      public class DevConfig {
      @Bean
      public DataSource dataSource() {
      return new DruidDataSource() {{
      setUrl("jdbc:mysql://dev.db:3306/test");
      }};
      }
      }

      // 生产环境配置类(仅当 prod 环境激活时生效)
      @Configuration
      @Profile("prod")
      public class ProdConfig {
      @Bean
      public DataSource dataSource() {
      return new DruidDataSource() {{
      setUrl("jdbc:mysql://prod.db:3306/test");
      }};
      }
      }

      如果标注@Profile("default"),是默认 profile,指定在默认环境下生效

      如果组件没有标注@Profile注解,代表任意时刻都生效

    • 标注在 @Bean 方法上

      示例:同一个配置类中,不同环境返回不同 Bean

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      @Configuration
      public class DataSourceConfig {

      // 开发环境 DataSource
      @Bean
      @Profile("dev")
      public DataSource devDataSource() {
      return new DruidDataSource() {{
      setUrl("jdbc:mysql://dev.db:3306/test");
      }};
      }

      // 生产环境 DataSource
      @Bean
      @Profile("prod")
      public DataSource prodDataSource() {
      return new DruidDataSource() {{
      setUrl("jdbc:mysql://prod.db:3306/test");
      }};
      }
      }
    • 组合条件:@Profile("!prod")(非生产环境生效)

      示例:测试、开发环境共用一套配置

      1
      2
      3
      4
      5
      6
      7
      @Bean
      @Profile("!prod") // 非 prod 环境(如 dev、test)生效
      public DataSource nonProdDataSource() {
      return new DruidDataSource() {{
      setUrl("jdbc:mysql://test.db:3306/test");
      }};
      }
    • application配置文件配合使用

      在 Spring Boot 中,通常会结合 application.yml 配置 Profiles

      application-{profile}.properties 可以作为指定环境的配置⽂件,示例:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      # 通用配置
      spring:
      datasource:
      driver-class-name: com.mysql.cj.jdbc.Driver

      # 开发环境配置(application-dev.yml)
      spring:
      profiles: dev
      datasource:
      url: jdbc:mysql://dev.db:3306/test
      username: dev_user
      password: dev_pass

      # 生产环境配置(application-prod.yml)
      spring:
      profiles: prod
      datasource:
      url: jdbc:mysql://prod.db:3306/test
      username: prod_user
      password: prod_pass

      激活这个环境,配置就会⽣效。最终⽣效的所有配置是

      • application.properties :主配置⽂件,任意时候都⽣效
      • application-{profile}.properties :指定环境配置⽂件,激活指定环境⽣效,带profile的配置文件的优先级 大于 不带的application

激活对应的 Profiles

Spring Profiles 的激活是实现环境隔离的关键环节,以下是各种激活方式的原理与实践

在配置文件中指定(推荐方式)

application.yml 中指定:

1
2
3
spring:
profiles:
active: prod # 激活生产环境配置
1
spring.profiles.active=prod

场景:

  • 项目默认环境(如测试环境)可直接写入 application.yml
  • 配合 application-{profile}.yml 实现环境隔离:

其中,可以实现指定多个环境

1
spring.profiles.active=dev,test

配置默认环境

1
spring.profiles.default=test

注意

  • 未标注 @Profile 的组件始终生效,不受默认环境影响。
  • 若同时存在 spring.profiles.activedefaultactive 优先级更高。

命令行参数激活(动态覆盖)

语法

1
2
3
4
5
# 单环境激活
java -jar app.jar --spring.profiles.active=prod

# 多环境激活(用逗号分隔)
java -jar app.jar --spring.profiles.active=dev,test

场景

  • 生产部署时临时切换环境(如从测试环境切到预发环境)。
  • 容器化部署时通过环境变量传递:

编程式动态激活(API 方式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;

public class DynamicProfileApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MainConfig.class);

// 方式1:直接设置激活的Profiles(覆盖其他配置)
app.setAdditionalProfiles("dev", "local");

// 方式2:通过EnvironmentPostProcessor扩展(更灵活)
app.addEnvironmentPostProcessor(new EnvironmentPostProcessor() {
@Override
public void postProcessEnvironment(ConfigurableEnvironment env, SpringApplication app) {
// 根据系统属性动态决定激活的环境
String envType = System.getProperty("app.env", "dev");
env.addActiveProfile(envType);
}
});

app.run(args);
}
}

或者在启动类中也可以指定

1
2
3
4
5
6
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MyApp.class);
// 动态设置激活的环境
app.setAdditionalProfiles("dev", "test");
app.run(args);
}

场景

  • 框架 / 中间件需要根据运行时条件动态选择环境(如根据服务器 IP 判断生产 / 测试环境)。

  • 单元测试中临时切换 Profile(配合@ActiveProfiles注解)。

激活方式的优先级(从高到低)

  1. 命令行参数--spring.profiles.active=prod(最高优先级,可覆盖一切)

  2. 编程式激活app.setAdditionalProfiles(...)(次高优先级)

  3. 系统属性-Dspring.profiles.active=prod

  4. 环境变量SPRING_PROFILES_ACTIVE=prod

  5. application.yml 中的 spring.profiles.active(默认配置)

  6. spring.profiles.default(最低优先级,仅当无 active 时生效)

配置文件加载顺序

Spring Boot 按以下顺序加载配置,后加载的会覆盖先加载的:

  1. 当前目录下的 application.yml
  2. 类路径下的 application.yml
  3. 当前目录下的 application-{profile}.yml
  4. 类路径下的 application-{profile}.yml

环境包含问题:

  • spring.profiles.activespring.profiles.default 这两条配置项目只能⽤到 ⽆ profile 的⽂件中,如果在 application-{profiles}.yaml这种带 profiles 的配置文件中编写就是⽆效的

  • 也可以额外添加⽣效⽂件,⽽不是激活替换。⽐如:

    1
    2
    spring.profiles.include[0]=common
    spring.profiles.include[1]=local
    1
    2
    3
    4
    5
    6
    spring:
    profiles:
    active: prod
    include:
    - common # 叠加common配置
    - logging # 叠加logging配置

    效果

    • 除了 prod 环境的配置,还会加载 application-common.ymlapplication-logging.yml
    • 适用于公共配置与环境配置分离的场景(如公共日志配置、公共服务配置)。

Profile 分组问题

在实际开发中,一个应用可能涉及多个维度的配置切换(如环境、功能模块),此时 Profile 分组 能更高效地管理复杂配置。Profile 分组允许将多个 Profile 归为一组,通过激活组名来批量启用相关配置

Profile 分组本质是将多个 Profile 逻辑聚合,实现 “一键激活多个关联配置”

1
2
3
4
5
6
7
8
# 开发环境分组配置
spring:
profiles: dev
profiles:
include:
- base # 继承基础配置
- redis # 启用 Redis 配置
- dev-logging # 开发环境专属日志配置
1
2
# 激活开发环境分组配置
java -jar app.jar --spring.profiles.active=dev

激活 dev 时,自动启用 baseredisdev-logging 三个 Profile 的配置。

Profile 分组支持嵌套,即一个分组可包含另一个分组

优先级规则

  • 同层级 Profile:后加载的覆盖先加载的(如 dev 覆盖 base 中的重复配置)。
  • 嵌套 Profile:子分组的配置会合并到父分组中,若冲突,以最内层为准。