Spring Profiles详解

1. 概述

在本教程中,我们将着重介绍 Spring 中的 Profiles。

Profiles是框架的核心功能——允许我们将 bean 映射到不同的Profile——例如,devtestprod

然后我们可以在不同的环境中激活不同的Profiles以仅引导我们需要的 bean。

2. 在 Bean 上使用 @Profile

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

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

我们用 @Profile(“dev”) 注解该 bean,它只会在开发期间出现在容器中。 在生产中,dev 根本不会激活:

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

作为快速旁注,Profile名称也可以使用 NOT 运算符作为前缀,例如 !dev,以将它们从Profile中排除。

在示例中,仅当 dev Profile未激活时才激活该组件:

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

3. 在 XML 中声明Profiles

Profiles can also be configured in XML. The tag has a profile attribute, which takes comma-separated values of the applicable profiles:

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

4. 设置 Profiles

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

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

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

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

这也是一个非常方便的位置,可以通过编程方式设置我们的激活的profiles:

@Configuration
public class MyWebApplicationInitializer 
  implements WebApplicationInitializer {

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

4.2. 通过 ConfigurableEnvironment 以编程方式

我们还可以直接在environment中设置profiles:

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

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

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

<context-param>
    <param-name>contextConfigLocationparam-name>
    <param-value>/WEB-INF/app-config.xmlparam-value>
context-param>
<context-param>
    <param-name>spring.profiles.activeparam-name>
    <param-value>devparam-value>
context-param>

4.4. JVM 系统参数

profile名称也可以通过 JVM 系统参数传入。 这些profile将在应用程序启动期间被激活:

-Dspring.profiles.active=dev

4.5. 环境变量

在 Unix 环境下,profiles 也可以通过环境变量激活

export SPRING_PROFILES_ACTIVE=dev
//或者
export spring_profiles_active=dev

4.6. Maven Profile

Spring Profile也可以通过 Maven Profile激活,方法是指定 spring.profiles.active 配置属性

在每个 Maven Profile中,我们可以设置一个 spring.profiles.active 属性:

<profiles>
    <profile>
        <id>devid>
        <activation>
            <activeByDefault>trueactiveByDefault>
        activation>
        <properties>
            <spring.profiles.active>devspring.profiles.active>
        properties>
    profile>
    <profile>
        <id>prodid>
        <properties>
            <spring.profiles.active>prodspring.profiles.active>
        properties>
    profile>
profiles>

它的值将用于替换 application.properties 文件中@spring.profiles.active@ 的占位符 :

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

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

<build>
    <resources>
        <resource>
            <filtering>truefiltering>
            <directory>src/main/resourcesdirectory>
            <includes>
              <include>public/**include>
              <include>static/**include>
              <include>templates/**include>
              <include>tpl/**include>
              <include>application.*include>
              <include>application-${spring.profiles.active}.*include>
              <include>log*-${spring.profiles.active}.xmlinclude>
            includes>
        resource>
    resources>
    ...
build>

并附加一个 -P 参数来切换将应用哪个 Maven Profile:

mvn clean package -Pprod

此命令将为 prod Profile打包应用程序。 它还会在应用程序运行时应用 spring.profiles.active 值为 prod

4.7. @ActiveProfile 在测试中

使用 @ActiveProfile 注解启用特定profile,测试可以很容易地指定哪些profile处于活动状态:

@ActiveProfiles("dev")

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

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

5. 默认的 Profile

任何未指定profile的 bean 都属于 default profile。

Spring 还提供了一种在没有其他profile处于活动状态时设置默认profile的方法—通过使用 spring.profiles.default 属性。

6. 获取活动Profile

Spring 的活动Profile驱动 @Profile 注解的行为以启用/禁用 beans。 但是,我们也可能希望以编程方式访问活动profile列表。

我们有两种方法,**使用 Environmentspring.profiles.active **。

6.1. 使用 Environment

我们可以通过注入 Environment 对象访问活动profiles:

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.profiles.active

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

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

在这里,我们的 activeProfile 变量将包含当前处于活动状态的profile的名称,如果有多个,它将包含以逗号分隔的名称。

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

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

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

现在,如果没有Profile处于活动状态,我们的 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. 示例:使用Profiles分离数据源配置

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

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

让我们创建一个公共接口 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 接口; 根据活动profile,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 中的Profiles

Spring Boot 支持到目前为止概述的所有profile配置,并具有一些附加功能。

8.1. 激活或设置一个Profile

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

spring.profiles.active=dev

但是,从 Spring Boot 2.4 开始,此属性不能与 spring.config.activate.on-profile 一起使用,因为这可能会引发 ConfigDataException InvalidConfigDataPropertyExceptionInactiveConfigDataAccessException)。

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

SpringApplication.setAdditionalProfiles("dev");

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

<plugins>
    <plugin>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-maven-pluginartifactId>
        <configuration>
            <profiles>
                <profile>devprofile>
            profiles>
        configuration>
    plugin>
    ...
plugins>

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

mvn spring-boot:run

8.2.特定于Profile的属性文件

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

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

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

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 Profile配置相同的属性,以使用内存中的 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 之前,可以从特定于Profile的文档中激活Profile。 但情况已不再如此; 对于更高版本,在这些情况下,框架将再次抛出 InvalidConfigDataPropertyExceptionInactiveConfigDataAccessException

8.3. 多文档文件

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

从 2.4 版开始,Spring Boot 除了以前支持的 YAML 之外,还扩展了对属性文件的多文档文件的支持。 所以现在,我们可以在同一个 *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. Profile 组

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

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

要通过我们的 application.properties 文件同时启用这些Profile,我们可以指定:

spring.profiles.group.production=proddb,prodquartz

因此,激活 production Profile也会激活 proddbprodquartz


<<<<<< [完] >>>>>>

你可能感兴趣的:(Spring,spring,java,jvm)