1. 概述

从 Spring Boot 1.3 开始,我们可以使用 EnvironmentPostProcessor 在应用上下文刷新之前对 Spring 的 Environment 进行自定义配置

本文将演示如何加载并转换自定义属性,将其注入到 Environment 中,并在后续代码中访问这些属性。

2. Spring 的 Environment

Spring 中的 Environment 抽象代表当前应用所运行的环境。它统一了多种属性源(property sources)的访问方式,包括 properties 文件、JVM 系统属性、系统环境变量、Servlet 上下文参数等。

通常来说,自定义 Environment 就是在这些属性暴露给我们的 Bean 之前对其进行操作。关于属性操作的更多内容,可以参考我们之前的文章:使用 Spring 操作属性

3. 一个简单示例

我们来构建一个价格计算应用。该应用可以根据第三方系统环境变量决定使用含税价(GROSS)还是净价(NET)进行计算。

3.1. 实现 EnvironmentPostProcessor

我们通过实现 EnvironmentPostProcessor 接口来读取环境变量:

calculation_mode=GROSS 
gross_calculation_tax_rate=0.15

然后通过这个处理器将它们以自定义前缀的形式暴露出来:

com.baeldung.environmentpostprocessor.calculation.mode=GROSS
com.baeldung.environmentpostprocessor.gross.calculation.tax.rate=0.15

接着,我们将这些属性添加进 Environment 中:

@Order(Ordered.LOWEST_PRECEDENCE)
public class PriceCalculationEnvironmentPostProcessor implements EnvironmentPostProcessor {

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, 
      SpringApplication application) {
        PropertySource<?> system = environment.getPropertySources()
          .get(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME);
        if (!hasOurPriceProperties(system)) {
          // 错误处理代码省略
        }
        Map<String, Object> prefixed = names.stream()
          .collect(Collectors.toMap(this::rename, system::getProperty));
        environment.getPropertySources()
          .addAfter(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, new MapPropertySource("prefixer", prefixed));
    }
}

上面代码做了几件事:

  • 获取系统环境变量的 PropertySource
  • 通过 system.getProperty 获取原始属性值(类似于 System.getenv().get
  • 构造一个新的 Map,将原始属性名转换为带前缀的名称(rename 方法省略)
  • 将新的 MapPropertySource 添加到 Environment

这样,当某个 Bean 请求 com.baeldung.environmentpostprocessor.calculation.mode 属性时,Environment 就会从我们新增的 PropertySource 中查找。

⚠️ 注意:EnvironmentPostProcessor 的文档建议我们实现 Ordered 接口或使用 @Order 注解来控制执行顺序。

这只是一个简单的属性源示例,实际上 Spring Boot 支持多种来源和格式的属性定制。

3.2. 在 spring.factories 中注册

为了让 Spring Boot 在启动时调用我们的实现,需要在 META-INF/spring.factories 文件中注册:

org.springframework.boot.env.EnvironmentPostProcessor=\
  com.baeldung.environmentpostprocessor.PriceCalculationEnvironmentPostProcessor

3.3. 使用 @Value 注入属性

我们可以在 Bean 中通过 @Value 注解使用这些属性:

public class GrossPriceCalculator implements PriceCalculator {
    @Value("${com.baeldung.environmentpostprocessor.gross.calculation.tax.rate}")
    double taxRate;

    @Override
    public double calculate(double singlePrice, int quantity) {
        // 实现逻辑省略
    }
}

这种用法与从 application.properties 中读取属性是一致的。

3.4. 在 Spring Boot 自动配置中使用属性

接下来我们看一个更复杂的例子:在自动配置中使用这些属性。

我们创建一个自动配置类,并根据属性值初始化不同的 PriceCalculator Bean:

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
public class PriceCalculationAutoConfig {
    @Bean
    @ConditionalOnProperty(name = 
      "com.baeldung.environmentpostprocessor.calculation.mode", havingValue = "NET")
    @ConditionalOnMissingBean
    public PriceCalculator getNetPriceCalculator() {
        return new NetPriceCalculator();
    }

    @Bean
    @ConditionalOnProperty(name = 
      "com.baeldung.environmentpostprocessor.calculation.mode", havingValue = "GROSS")
    @ConditionalOnMissingBean
    public PriceCalculator getGrossPriceCalculator() {
        return new GrossPriceCalculator();
    }
}

⚠️ 同样地,这个自动配置类也需要在 META-INF/spring.factories 中注册:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.baeldung.environmentpostprocessor.autoconfig.PriceCalculationAutoConfig

因为 EnvironmentPostProcessor 是在 Spring Boot 自动配置之前运行的,所以这种组合可以极大增强自动配置的灵活性。

更多关于 Spring Boot 自动配置的内容,可以阅读我们的文章:Spring Boot 自定义自动配置

4. 测试自定义实现

我们可以设置系统环境变量来测试这段代码。

在 Windows 上:

set calculation_mode=GROSS
set gross_calculation_tax_rate=0.15

在 Linux/Unix 上:

export calculation_mode=GROSS 
export gross_calculation_tax_rate=0.15

然后使用以下命令启动应用:

mvn spring-boot:run \
  -Dstart-class=com.baeldung.environmentpostprocessor.PriceCalculationApplication \
  -Dspring-boot.run.arguments="100,4"

5. 总结

EnvironmentPostProcessor 允许我们从各种来源加载任意格式的配置文件,并进行必要的转换,使其在 Environment 中可用,为后续 Bean 初始化提供支持。

这种机制在将 Spring Boot 应用与第三方配置系统集成时非常有用。

完整源码可在 GitHub 获取:GitHub 仓库链接


原始标题:EnvironmentPostProcessor in Spring Boot