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 仓库链接