Перейти к основному содержимому

EnvironmentPostProcessor в Spring Boot

· 4 мин. чтения

1. Обзор

Начиная с Spring Boot 1.3, мы можем использовать EnvironmentPostProcessor для настройки среды приложения до обновления контекста приложения .

В этом руководстве мы рассмотрим, как загрузить и преобразовать пользовательские свойства в среду, а затем получить доступ к этим свойствам .

2. Весенняя среда

Абстракция Environment в Spring представляет среду, в которой работает текущее приложение. В то же время он имеет тенденцию к унификации способов доступа к свойствам в различных источниках свойств, таких как файлы свойств, системные свойства JVM, системные переменные среды и параметры контекста сервлета.

Таким образом, в большинстве случаев настройка среды означает манипулирование различными свойствами до того, как они станут доступными для наших компонентов. Для начала посетите нашу предыдущую статью об управлении свойствами с помощью Spring .

3. Краткий пример

Давайте теперь создадим простое приложение для расчета цен. Он рассчитает цену либо в валовом, либо в нетто-режиме. Переменные системной среды от третьей стороны будут определять, какой режим расчета выбрать.

3.1. Реализация EnvironmentPostProcessor

Для этого реализуем интерфейс EnvironmentPostProcessor .

Мы будем использовать его для чтения нескольких переменных среды:

calculation_mode=GROSS 
gross_calculation_tax_rate=0.15

И мы будем использовать постпроцессор, чтобы выставить их в зависимости от приложения, в данном случае с пользовательским префиксом:

com.foreach.environmentpostprocessor.calculation.mode=GROSS
com.foreach.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)) {
// 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));
}

}

Давайте посмотрим, что мы здесь сделали. Во- первых, мы попросили среду предоставить нам PropertySource для переменных среды. Вызов результирующего system.getProperty подобен вызову Java System.getenv().get.

Затем, пока эти свойства существуют в среде, мы создадим новую карту с префиксом. Для краткости мы пропустим содержимое rename , но ознакомьтесь с примером кода для полной реализации. Результирующая карта имеет те же значения, что и system , но с префиксными ключами.

Наконец, мы добавим наш новый PropertySource в среду. Теперь, если бин запрашивает com.foreach.environmentpostprocessor.calculation.mode , Environment сверится с нашей картой.

Обратите внимание, кстати, что Javadoc EnvironmentPostProcessor рекомендует нам либо реализовать интерфейс Ordered , либо использовать аннотацию @Order .

И это, конечно, только один источник свойств . Spring Boot позволяет нам работать с многочисленными источниками и форматами.

3.2. Регистрация на весенние фабрики

Чтобы вызвать реализацию в процессе начальной загрузки Spring Boot, нам нужно зарегистрировать класс в META-INF/spring.factories :

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

3.3. Доступ к свойствам с помощью аннотации @Value

Давайте использовать их в паре классов. В примере у нас есть интерфейс PriceCalculator с двумя реализациями: GrossPriceCalculator и NetPriceCalculator.

В наших реализациях мы можем просто использовать @Value для получения наших новых свойств:

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

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

Это удобно, так как мы получаем доступ к любым другим свойствам, таким как те, которые мы определили в application.properties.

3.4. Доступ к свойствам в автонастройке Spring Boot

Теперь давайте рассмотрим сложный случай, когда мы обращаемся к предыдущим свойствам в автонастройке Spring Boot.

Мы создадим класс автоконфигурации для чтения этих свойств. Этот класс будет инициализировать и связывать bean-компоненты в контексте приложения в соответствии с различными значениями свойств:

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

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

Подобно реализации EnvironmentPostProcessor , класс автоконфигурации также должен быть зарегистрирован в META-INF/spring.factories :

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

Это работает, потому что пользовательские реализации EnvironmentPostProcessor срабатывают раньше, чем автоконфигурация Spring Boot . Эта комбинация делает автоконфигурацию Spring Boot более мощной.

И, чтобы узнать больше об автонастройке Spring Boot, ознакомьтесь со статьей Custom Auto-Configuration with 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 :

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

5. Вывод

Подводя итог, реализация EnvironmentPostProcessor способна загружать произвольные файлы в различных форматах из разных мест. Кроме того, мы можем выполнить любое преобразование, необходимое для того, чтобы сделать свойства доступными в среде для последующего использования. Эта свобода, безусловно, полезна, когда мы интегрируем приложение на основе Spring Boot со сторонними конфигурациями.

Исходный код можно найти в репозитории GitHub .