Contents

Spring 中的 Profiles

1.概述

在本教程中,我们将重点介绍 Spring 中的 Profiles。 配置文件是框架的核心特性——允许我们将 bean 映射到不同的配置文件——例如devtestprod。 然后,我们可以在不同的环境中激活不同的配置文件以仅引导我们需要的 bean。

2.在 Bean 上使用*@Profile*

让我们从简单的开始,看看我们如何让一个 bean 属于一个特定的配置文件。我们使用@Profile*注释——我们将 bean 映射到那个特定的配置文件*;注释仅采用一个(或多个)配置文件的名称。

考虑一个基本场景:我们有一个 bean,它应该只在开发期间处于活动状态,而不是在生产中部署。

我们使用dev配置文件注释该 bean  ,它只会在开发期间出现在容器中。在生产中,开发人员根本不会处于活动状态:

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

作为一个快速的旁注,配置文件名称也可以使用 NOT 运算符作为前缀,例如*!dev*,以将它们从配置文件中排除。 在示例中,仅当dev 配置文件未激活时才会激活组件:

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

3. 在 XML 中声明配置文件

配置文件也可以在 XML 中配置。<beans>标签有一个profile属性,它采用逗号分隔的适用配置文件的值:

<beans profile="dev">
    <bean id="devDatasourceConfig" 
      class="com.blogdemo.profiles.DevDatasourceConfig" />
</beans>

4. 设置配置文件

下一步是激活和设置配置文件,以便在容器中注册相应的 bean。

这可以通过多种方式完成,我们将在以下部分中进行探讨。

4.1. 通过WebApplicationInitializer接口以编程方式

在 Web 应用程序中,WebApplicationInitializer可用于以编程方式配置ServletContext

它也是以编程方式设置我们的活动配置文件的非常方便的位置:

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

4.2. 通过ConfigurableEnvironment以编程方式

我们还可以直接在环境中设置配置文件:

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

4.3. web.xml中的上下文参数

同样,我们可以使用上下文参数在 Web 应用程序的web.xml文件中定义活动配置文件:

<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 系统参数传入。这些配置文件将在应用程序启动期间激活:

-Dspring.profiles.active=dev

4.5. 环境变量

在 Unix 环境中,配置文件也可以通过环境变量激活

export spring_profiles_active=dev

4.6. Maven 简介

Spring 配置文件也可以通过 Maven 配置文件激活,通过指定spring.profiles.active配置属性。 在每个 Maven 配置文件中,我们可以设置一个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=@spring.profiles.active@

现在我们需要在pom.xml中启用资源过滤:

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

并附加一个*-P*参数来切换将应用哪个 Maven 配置文件:

mvn clean package -Pprod

此命令将为prod配置文件打包应用程序。它还 在此应用程序运行时应用spring.profiles.activeprod  。

4.7. 测试中的*@ActiveProfile*

测试可以很容易地使用*@ActiveProfile*注释指定哪些配置文件处于活动状态以启用特定配置文件:

@ActiveProfiles("dev")

到目前为止,我们已经研究了多种激活配置文件的方法。现在让我们看看哪个优先级高于另一个,如果我们使用多个优先级会发生什么,从最高到最低优先级:

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

5. 默认配置文件

任何未指定配置文件的 bean 都属于默认 配置文件。 Spring 还提供了一种在没有其他配置文件处于活动状态时设置默认配置文件的方法 - 通过使用spring.profiles.default 属性。

6. 获取活跃的个人资料

Spring 的活动配置文件驱动*@Profile*注释的行为以启用/禁用 bean。但是,我们也可能希望以编程方式访问活动配置文件列表。

我们有两种方法可以做到这一点,使用Environmentspring.active.profile

6.1. 使用Environment

我们可以通过注入Environment对象来访问活动配置文件:

public class ProfileManager {
    @Autowired
    private Environment environment;
    public void getActiveProfiles() {
        for (String profileName : environment.getActiveProfiles()) {
            System.out.println("Currently active profile - " + profileName);
        }  
    }
}

6.2. 使用spring.active.profile

或者,我们可以通过注入属性 spring.profiles.active来访问配置文件:

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

在这里,我们的activeProfile 变量**将包含当前活动的配置文件的名称,**如果有多个,它将包含用逗号分隔的名称。

但是,我们应该**考虑如果根本没有活动配置文件会发生什么。**使用我们上面的代码,缺少活动配置文件会阻止创建应用程序上下文。由于缺少用于注入变量的占位符,这将导致IllegalArgumentException

为了避免这种情况,我们可以定义一个默认值

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

现在,如果没有激活的配置文件,我们的activeProfile将只包含一个空字符串。 如果我们想像前面的例子一样访问它们的列表,我们可以通过拆分 activeProfile变量来实现:

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. 示例:使用配置文件分离数据源配置

既然基础知识已经结束,让我们看一个真实的例子。

考虑一个场景,我们必须为开发和生产环境维护数据源配置

让我们创建一个需要由两个数据源实现实现的通用接口DatasourceConfig

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. ");
    }
}

现在让我们创建一个测试并注入我们的 DatasourceConfig 接口;根据活动配置文件,Spring 将注入DevDatasourceConfigProductionDatasourceConfig bean:

public class SpringProfilesWithMavenPropertiesIntegrationTest {
    @Autowired
    DatasourceConfig datasourceConfig;
    public void setupDatasource() {
        datasourceConfig.setup();
    }
}

dev  profile 处于活动状态时,Spring 注入DevDatasourceConfig对象,然后调用*setup()*方法时,输出如下:

Setting up datasource for DEV environment.

8. Spring Boot 中的配置文件

Spring Boot 支持到目前为止列出的所有配置文件配置,并具有一些附加功能。

8.1. 激活或设置配置文件

第 4 节中介绍的初始化参数spring.profiles.active也可以设置为 Spring Boot 中的属性,以定义当前活动的配置文件。这是 Spring Boot 将自动获取的标准属性:

spring.profiles.active=dev

但是,从 Spring Boot 2.4 开始,此属性不能与spring.config.activate.on-profile结合使用,因为这可能会引发ConfigDataException (即InvalidConfigDataPropertyException或InactiveConfigDataAccessException*)。

要以编程方式设置配置文件,我们还可以使用SpringApplication类:

SpringApplication.setAdditionalProfiles("dev");

要在 Spring Boot 中使用 Maven 设置配置文件,我们可以 在pom.xml中的spring-boot-maven-plugin下指定配置文件名称:

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

并执行 Spring Boot 特定的 Maven 目标:

mvn spring-boot:run

8.2. 配置文件特定的属性文件

但是,Spring Boot 带来的最重要的配置文件相关功能是**配置文件特定的属性文件。**这些必须以application-{profile}.properties格式命名。

Spring Boot 将自动为所有配置文件加载application.properties文件中的属性,并且仅为指定配置文件加载特定于配置文件的*.properties*文件中的属性。

例如,我们可以使用名为application-dev.propertiesapplication-production.properties的两个文件为*dev production *配置文件配置不同的数据源:

application-production.properties文件中,我们可以设置一个MySql数据源:

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文件中为dev配置文件配置相同的属性,以使用内存H2数据库:

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 之前,可以从特定于配置文件的文档中激活配置文件。但情况已不再如此;对于更高版本, 在这些情况下,框架将再次抛出InvalidConfigDataPropertyException或 InactiveConfigDataAccessException

8.3. 多文档文件

为了进一步简化为不同环境定义属性,我们甚至可以将所有属性合并到同一个文件中,并使用分隔符来指示配置文件。

从 2.4 版本开始,除了之前支持的YAML 之外,Spring Boot 还扩展了对属性文件的多文档文件的支持。所以现在,我们可以在同一个application.properties中指定devproduction属性

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

此文件由 Spring Boot 按从上到下的顺序读取。也就是说,如果某个属性,比如my.prop,在上述示例的末尾再次出现,则将考虑最后的值。

8.4. 配置文件组

Boot 2.4 中添加的另一个功能是配置文件组。顾名思义,它允许我们将相似的配置文件组合在一起

让我们考虑一个用例,其中我们有多个用于生产环境的配置文件。比如说,一个用于数据库的proddb和一个用于production 环境中调度程序的prodquartz

要通过我们的application.properties文件一次性启用这些配置文件,我们可以指定:

spring.profiles.group.production=proddb,prodquartz

因此,激活production 配置文件也将激活proddbprodquartz