1. 概述

本篇文章将深入介绍 Spring 中的 Profiles 功能。

Profiles 是 Spring 框架的核心特性之一,它允许我们将 Bean 映射到不同的运行环境,例如开发环境(dev)、测试环境(test)和生产环境(prod)。

通过激活不同的 Profile,我们可以在不同环境中只加载所需的 Bean,从而实现配置的灵活切换。

2. 使用 @Profile 注解标记 Bean

最基础的做法是使用 @Profile 注解来指定某个 Bean 属于哪个 Profile。这个注解可以接受一个或多个 Profile 名称。

举个例子:我们有一个 Bean 只在开发环境使用,生产环境不需要部署它。

这时我们可以给这个 Bean 加上 @Profile("dev") 注解,这样它就只会在开发环境被注册进容器中:

@Component
@Profile("dev")
public class DevDatasourceConfig

⚠️ 小技巧:Profile 名称也可以加上 ! 前缀表示“非”,例如 !dev 表示“除了 dev 以外的所有环境”。

如下代码所示,该组件只有在 dev Profile 未激活时才会生效:

@Component
@Profile("!dev")
public class DevDatasourceConfig

3. 在 XML 中声明 Profile

除了注解方式外,Profile 也可以在 XML 配置文件中定义。<beans> 标签支持 profile 属性,用于指定该配置块适用的 Profile:

<beans profile="dev">
    <bean id="devDatasourceConfig" 
      class="org.baeldung.profiles.DevDatasourceConfig" />
</beans>

4. 设置激活的 Profile

有了 Profile 的定义后,下一步就是告诉 Spring 当前应该激活哪个 Profile,以便注册对应的 Bean。

有多种方式可以设置激活的 Profile,下面我们逐一介绍。

4.1. 通过 WebApplicationInitializer 编程设置

在 Web 应用中,可以通过实现 WebApplicationInitializer 接口,在初始化 ServletContext 时设置激活的 Profile:

@Configuration
public class MyWebApplicationInitializer 
  implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
 
        servletContext.setInitParameter(
          "spring.profiles.active", "dev");
    }
}

4.2. 通过 ConfigurableEnvironment 编程设置

我们也可以直接通过注入的 ConfigurableEnvironment 来设置:

@Autowired
private ConfigurableEnvironment env;
...
env.setActiveProfiles("someProfile");

4.3. 在 web.xml 中通过上下文参数设置

在传统的 Web 应用中,可以在 web.xml 中通过上下文参数来设置激活的 Profile:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/app-config.xml</param-value>
</context-param>
<context-param>
    <param-name>spring.profiles.active</param-name>
    <param-value>dev</param-value>
</context-param>

4.4. 通过 JVM 系统参数设置

在启动应用时,可以通过 JVM 参数传递 Profile 名称:

-Dspring.profiles.active=dev

4.5. 通过环境变量设置

在 Unix 环境中,可以通过环境变量来激活 Profile:

export spring_profiles_active=dev

4.6. 通过 Maven Profile 设置

Spring Profile 也可以通过 Maven Profile 来激活。在每个 Maven Profile 中,我们可以设置 spring.profiles.active 属性:

<profiles>
    <profile>
        <id>dev</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
            <spring.profiles.active>dev</spring.profiles.active>
        </properties>
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <spring.profiles.active>prod</spring.profiles.active>
        </properties>
    </profile>
</profiles>

然后在 application.properties 中使用占位符:

spring.profiles.active=@spring.profiles.active@

接下来需要在 pom.xml 中开启资源过滤:

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
        </resource>
    </resources>
    ...
</build>

最后通过 Maven 命令指定 Profile 打包:

mvn clean package -Pprod

这会将应用打包为生产环境配置,并自动激活 prod Profile。

4.7. 在测试中使用 @ActiveProfiles

在测试中,可以使用 @ActiveProfiles 注解来指定激活的 Profile:

@ActiveProfiles("dev")

4.8. Profile 激活优先级

当多个地方都设置了 Profile 时,它们的优先级如下(从高到低):

  1. web.xml 中的上下文参数
  2. WebApplicationInitializer
  3. JVM 系统参数
  4. 环境变量
  5. Maven Profile

5. 默认 Profile

所有没有指定 Profile 的 Bean 都属于 default Profile。

我们可以通过 spring.profiles.default 属性来设置默认 Profile,当没有其他 Profile 被激活时,这些 Bean 会自动生效。

6. 获取当前激活的 Profile

有时候我们需要在代码中动态获取当前激活的 Profile,Spring 提供了两种方式。

6.1. 使用 Environment 获取

注入 Environment 后,可以通过 getActiveProfiles() 方法获取:

public class ProfileManager {
    @Autowired
    private Environment environment;

    public void getActiveProfiles() {
        for (String profileName : environment.getActiveProfiles()) {
            System.out.println("Currently active profile - " + profileName);
        }  
    }
}

6.2. 使用 @Value 注入属性

也可以通过注入 spring.profiles.active 属性来获取:

@Value("${spring.profiles.active}")
private String activeProfile;

⚠️ 注意:如果没有激活的 Profile,会导致占位符注入失败,抛出 IllegalArgumentException

为避免这个问题,可以设置默认值:

@Value("${spring.profiles.active:}")
private String activeProfile;

如果需要获取多个 Profile,可以使用字符串分割:

public class ProfileManager {
    @Value("${spring.profiles.active:}")
    private String activeProfiles;

    public String getActiveProfiles() {
        for (String profileName : activeProfiles.split(",")) {
            System.out.println("Currently active profile - " + profileName);
        }
    }
}

7. 示例:使用 Profile 区分数据源配置

下面通过一个实际例子来看如何使用 Profile。

假设我们需要为开发和生产环境分别配置不同的数据源。

首先定义一个通用接口:

public interface DatasourceConfig {
    public void setup();
}

开发环境的实现:

@Component
@Profile("dev")
public class DevDatasourceConfig implements DatasourceConfig {
    @Override
    public void setup() {
        System.out.println("Setting up datasource for DEV environment. ");
    }
}

生产环境的实现:

@Component
@Profile("production")
public class ProductionDatasourceConfig implements DatasourceConfig {
    @Override
    public void setup() {
       System.out.println("Setting up datasource for PRODUCTION environment. ");
    }
}

在测试中注入接口,Spring 会根据激活的 Profile 自动注入对应的实现类:

public class SpringProfilesWithMavenPropertiesIntegrationTest {
    @Autowired
    DatasourceConfig datasourceConfig;

    public void setupDatasource() {
        datasourceConfig.setup();
    }
}

如果当前激活的是 dev Profile,输出如下:

Setting up datasource for DEV environment.

8. Spring Boot 中的 Profile

Spring Boot 支持前面介绍的所有 Profile 配置方式,并且还提供了额外的功能。

8.1. 激活 Profile

在 Spring Boot 中,可以通过 application.properties 文件设置激活的 Profile:

spring.profiles.active=dev

⚠️ 从 Spring Boot 2.4 开始,该属性不能与 spring.config.activate.on-profile 同时使用,否则会抛出 ConfigDataException

我们也可以通过编程方式设置:

SpringApplication.setAdditionalProfiles("dev");

在 Maven 中,也可以通过 spring-boot-maven-plugin 插件指定 Profile:

<plugins>
    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
            <profiles>
                <profile>dev</profile>
            </profiles>
        </configuration>
    </plugin>
    ...
</plugins>

执行以下命令启动应用:

mvn spring-boot:run

8.2. Profile 特定的配置文件

Spring Boot 最强大的功能之一就是支持 Profile 特定的配置文件,文件名格式为:application-{profile}.properties

Spring Boot 会自动加载通用配置文件 application.properties 和特定 Profile 的配置文件。

例如,为 devproduction 分别配置不同的数据源:

application-production.properties

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/db
spring.datasource.username=root
spring.datasource.password=root

application-dev.properties

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

✅ 这样就能轻松实现不同环境的配置隔离。

⚠️ 从 Spring Boot 2.4 开始,Profile 特定的配置文件不能再激活其他 Profile,否则会抛出异常。

8.3. 多文档配置文件

从 Spring Boot 2.4 开始,支持在单个 .properties 文件中使用 --- 分隔符定义多个 Profile 的配置:

my.prop=used-always-in-all-profiles
#---
spring.config.activate.on-profile=dev
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/db
spring.datasource.username=root
spring.datasource.password=root
#---
spring.config.activate.on-profile=production
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa

✅ 配置会从上到下依次加载,后出现的属性会覆盖前面的同名属性。

8.4. Profile 组

Spring Boot 2.4 引入了 Profile 组功能,可以将多个 Profile 聚合成一个组:

spring.profiles.group.production=proddb,prodquartz

激活 production Profile 时,会自动激活 proddbprodquartz

9. 总结

本文介绍了如何使用 @Profile 注解定义 Profile,以及如何在不同场景下激活 Profile。并通过一个简单的实际例子展示了 Profile 的使用方式。

完整示例代码可参考 GitHub 项目


原始标题:Spring Profiles | Baeldung