EnvironmentPostProcessor na Inicialização Spring

EnvironmentPostProcessor na Inicialização Spring

1. Visão geral

A partir do Spring Boot 1.3, somos capazes deuse the EnvironmentPostProcessor tocustomize the application’s Environment before application context is refreshed.

Neste tutorial, vamos dar uma olhada em como carregar e transformar as propriedades personalizadas emEnvironment,e, em seguida, acessar essas propriedades.

2. PrimaveraEnvironment

A abstraçãoEnvironment no Spring representa o ambiente no qual o aplicativo atual está sendo executado. Enquanto isso, tende a unificar as maneiras de acessar propriedades em uma variedade de fontes de propriedades, como arquivos de propriedades, propriedades do sistema JVM, variáveis ​​de ambiente do sistema e parâmetros de contexto do servlet.

So in most cases, customizing the Environment means manipulation of various properties before they’re exposed to our beans. Para começar, visite nosso artigo anterioron manipulating properties with Spring.

3. Um Exemplo Rápido

Vamos agora construir um aplicativo simples de cálculo de preço. Ele vai calcular o preço no modo baseado em bruto ou líquido. As variáveis ​​de ambiente do sistema de terceiros determinarão qual modo de cálculo escolher.

3.1. ImplementandoEnvironmentPostProcessor

Para fazer isso, vamos implementar a interfaceEnvironmentPostProcessor.

Vamos usá-lo para ler algumas variáveis ​​de ambiente:

calculation_mode=GROSS
gross_calculation_tax_rate=0.15

E usaremos o pós-processador para expô-los de uma forma específica do aplicativo, neste caso com um prefixo personalizado:

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

Então, podemos simplesmente adicionar nossas novas propriedades emEnvironment:

@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 prefixed = names.stream()
          .collect(Collectors.toMap(this::rename, system::getProperty));
        environment.getPropertySources()
          .addAfter(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, new MapPropertySource("prefixer", prefixed));
    }

}

Vamos ver o que fizemos aqui. Primeiro,we asked environment to give us the PropertySource for environment variables. Chamarsystem.getProperty resultante é semelhante a chamarSystem.getenv().get. de Java

Então, desde que essas propriedades existam no ambiente,we’ll create a new map,prefixed. Para resumir, vamos pular o conteúdo derename, mas verificar o exemplo de código para a implementação completa. The resulting map has the same values as system, but with prefixed keys.

Finalmente, adicionaremos nosso novoPropertySource aoEnvironment. Agora, se um bean solicitarcom.example.environmentpostprocessor.calculation.mode, oEnvironment consultará nosso mapa.

Observe, a propósito, queEnvironmentPostProcessor‘s Javadoc nos incentiva a implementar a interfaceOrdered ouuse the @Order annotation.

E isso é, é claro,just a single property source. Spring Boot nos permite atender a várias fontes e formatos.

3.2. Registro nospring.factories

Para invocar a implementação no processo de bootstrap Spring Boot, precisamos registrar a classe emMETA-INF/spring.factories:

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

3.3. Acesse as propriedades usando a anotação@Value

Vamos usá-los em algumas aulas. Na amostra, temos uma sinterfacePriceCalculator com duas implementações:GrossPriceCalculatoreNetPriceCalculator.

Em nossas implementações,we can just use @Value to retrieve our new properties:

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

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

Isso é bom, pois é a mesma maneira que acessamos quaisquer outras propriedades, como aquelas que definimos emapplication.properties.

3.4. Acesse as propriedades na configuração automática do Spring Boot

Agora, vamos ver um caso complexo em que acessamos as propriedades anteriores na autoconfiguração do Spring Boot.

Vamos criar a classe de configuração automática para ler essas propriedades. Essa classe inicializará e conectará os beans no contexto do aplicativo de acordo com os diferentes valores de propriedade:

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

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

Semelhante à implementação deEnvironmentPostProcessor, a classe de autoconfiguração também precisa ser registrada emMETA-INF/spring.factories:

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

Isso funciona porquecustom EnvironmentPostProcessor implementations kick in before Spring Boot autoconfiguration does. Essa combinação torna a configuração automática da inicialização Spring mais poderosa.

E, para obter mais detalhes sobre a configuração automática do Spring Boot, dê uma olhada no artigo emCustom Auto-Configuration with Spring Boot.

4. Teste a implementação personalizada

Agora é hora de testar nosso código. Podemos definir as variáveis ​​de ambiente do sistema no Windows executando:

set calculation_mode=GROSS
set gross_calculation_tax_rate=0.15

Ou no Linux / Unix, podemosexport em vez disso:

export calculation_mode=GROSS
export gross_calculation_tax_rate=0.15

Depois disso, podemos iniciar o teste com o comandomvn spring-boot:run:

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

5. Conclusão

To sum up, the EnvironmentPostProcessor implementation is able to load arbitrary files in a variety of formats from different locations. Além disso, podemos fazer qualquer transformação necessária para tornar as propriedades prontamente disponíveis emEnvironment para uso posterior. Essa liberdade é certamente útil quando integramos o aplicativo baseado no Spring Boot às configurações de terceiros.

O código-fonte pode ser encontrado emthe GitHub repository.