EnvironmentPostProcessor in Spring Boot – Spring Boot中的EnvironmentPostProcessor

最后修改: 2019年 4月 8日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

As of Spring Boot 1.3, we’re able to use the EnvironmentPostProcessor to customize the application’s Environment before application context is refreshed.

从Spring Boot 1.3开始,我们能够使用EnvironmentPostProcessor 在应用上下文刷新之前定制应用的Environment

In this tutorial, let’s take a look at how to load and transform the custom properties into the Environment, and then access those properties.

在本教程中,让我们看看如何将自定义属性加载和转换到环境中,然后访问这些属性

2. Spring Environment

2.Spring环境

The Environment abstraction in Spring represents the environment in which the current application is running.  In the meanwhile, it tends to unify the ways to access properties in a variety of property sources, such as properties files, JVM system properties, system environment variables, and servlet context parameters.

Spring中的Environment抽象代表了当前应用程序所处的环境。 同时,它倾向于统一访问各种属性源的方法,如属性文件、JVM系统属性、系统环境变量和Servlet上下文参数。

So in most cases, customizing the Environment means manipulation of various properties before they’re exposed to our beans. To start, please visit our previous article on manipulating properties with Spring.

因此,在大多数情况下,定制Environment意味着在将各种属性暴露给我们的Bean之前对其进行操作。要想开始,请访问我们之前的文章关于用Spring操作属性

3. A Quick Example

3.一个快速的例子

Let’s now build a simple price calculation application. It’ll calculate the price in either gross-based or net-based mode. The system environment variables from a third party will determine which calculation mode to choose.

现在让我们建立一个简单的价格计算应用程序。它将以基于毛额或基于净额的模式计算价格。来自第三方的系统环境变量将决定选择哪种计算模式。

3.1. Implementing EnvironmentPostProcessor

3.1.实现EnvironmentPostProcessor

To do this, let’s implement the EnvironmentPostProcessor interface.

要做到这一点,让我们实现EnvironmentPostProcessor接口。

We’ll use it to read a couple of environment variables:

我们将用它来读取几个环境变量。

calculation_mode=GROSS 
gross_calculation_tax_rate=0.15

And we’ll use the post-processor to expose these in an application-specific way, in this case with a custom prefix:

而我们将使用后处理程序以特定于应用程序的方式暴露这些信息,在这种情况下,我们将使用一个自定义的前缀。

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

Then, we can quite simply add our new properties into the Environment:

然后,我们可以很简单地将我们的新属性添加到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)) {
          // error handling code omitted
        }
        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));
    }

}

Let’s see what we’ve done here. First, we asked environment to give us the PropertySource for environment variables. Calling the resulting system.getProperty is similar to calling Java’s System.getenv().get.

让我们看看我们在这里做了什么。首先,我们要求environment为我们提供环境变量的PropertySource调用产生的system.getProperty类似于调用Java的System.getenv().get。

Then, so long as those properties exist in the environment, we’ll create a new map, prefixed. For brevity, we’ll skip the contents of rename, but check out the code sample for the complete implementation. The resulting map has the same values as system, but with prefixed keys.

然后,只要这些属性存在于环境中,我们将创建一个新的地图,prefixed.为了简洁起见,我们将跳过rename的内容,但请查看代码示例以了解完整的实现。所产生的映射具有与system相同的值,但有前缀的键。

Finally, we’ll add our new PropertySource to the Environment. Now, if a bean asks for com.baeldung.environmentpostprocessor.calculation.mode, the Environment will consult our map.

最后,我们将新的PropertySource添加到Environment.中。现在,如果一个Bean询问com.baeldung.environmentpostprocessor.calculation.modeEnvironment将查阅我们的地图。

Note, by the way, that EnvironmentPostProcessor‘s Javadoc encourages us to either implement the Ordered interface or use the @Order annotation.

顺便注意,EnvironmentPostProcessor的Javadoc鼓励我们要么实现Ordered 接口,要么使用@Order annotation

And this is, of course, just a single property source. Spring Boot allows us to cater to numerous sources and formats.

当然,这只是一个单一的财产来源Spring Boot允许我们满足众多来源和格式。

3.2. Registration in the spring.factories

3.2.在spring.plants中注册

To invoke the implementation in the Spring Boot bootstrap process, we need to register the class in the META-INF/spring.factories:

为了在Spring Boot bootstrap过程中调用该实现,我们需要在META-INF/spring. factories中注册该类。

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

3.3. Access the Properties Using @Value Annotation

3.3.使用@Value注解访问属性

Let’s use these in a couple of classes. In the sample, we’ve got a PriceCalculator interface with two implementations: GrossPriceCalculator and NetPriceCalculator.

让我们在几个类中使用这些。在样本中,我们有一个PriceCalculator接口,有两个实现。GrossPriceCalculatorNetPriceCalculator。

In our implementations, we can just use @Value to retrieve our new properties:

在我们的实现中,我们可以直接使用@Value来检索我们的新属性:

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

    @Override
    public double calculate(double singlePrice, int quantity) {
        //calcuation implementation omitted
    }
}

This is nice as it’s the same way we access any other properties, like those we’ve defined in application.properties.

这很好,因为它与我们访问任何其他属性的方式相同,比如我们在application.properties.中定义的那些。

3.4. Access the Properties in Spring Boot Auto-configuration

3.4.访问Spring Boot自动配置中的属性

Now, let’s see a complex case where we access the preceding properties in Spring Boot autoconfiguration.

现在,让我们看看一个复杂的案例,我们在Spring Boot自动配置中访问前面的属性。

We’ll create the autoconfiguration class to read those properties. This class will initialize and wire the beans in the application context according to the different property values:

我们将创建自动配置类来读取这些属性。这个类将根据不同的属性值来初始化和连接应用上下文中的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();
    }
}

Similar to the EnvironmentPostProcessor implementation, the autoconfiguration class needs to be registered in the META-INF/spring.factories as well:

EnvironmentPostProcessor实现类似,自动配置类也需要在META-INF/spring.plants中注册。

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

This works because custom EnvironmentPostProcessor implementations kick in before Spring Boot autoconfiguration does. This combination makes Spring Boot autoconfiguration more powerful.

这是因为定制的EnvironmentPostProcessor实现在Spring Boot自动配置之前启动。这种组合使Spring Boot的自动配置功能更加强大。

And, for more specifics about Spring Boot autoconfiguration, please have a look at the article on Custom Auto-Configuration with Spring Boot.

而且,关于Spring Boot自动配置的更多细节,请看Custom Auto-Configuration with Spring Boot的文章。

4. Test the Custom Implementation

4.测试定制的实现

Now it’s time to test our code. We can set the system environment variables in Windows by running:

现在是时候测试我们的代码了。我们可以在Windows中通过运行来设置系统环境变量。

set calculation_mode=GROSS
set gross_calculation_tax_rate=0.15

Or in Linux/Unix, we can export them instead:

或者在Linux/Unix中,我们可以用导出代替它们。

export calculation_mode=GROSS 
export gross_calculation_tax_rate=0.15

After that, we could start the test with the mvn spring-boot:run command:

之后,我们可以用mvn spring-boot:run命令启动测试。

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

5. Conclusion

5.总结

To sum up, the EnvironmentPostProcessor implementation is able to load arbitrary files in a variety of formats from different locations. In addition, we can do any transformation we need to make the properties readily available in the Environment for later use. This freedom is certainly useful when we integrate Spring Boot-based application with the third-party configurations.

综上所述,EnvironmentPostProcessor实现能够从不同位置加载各种格式的任意文件。此外,我们可以进行任何需要的转换,使属性在Environment中随时可用,以供日后使用。当我们将基于Spring Boot的应用程序与第三方配置集成时,这种自由度当然很有用。

The source code can be found in the GitHub repository.

源代码可以在GitHub资源库中找到。