1. 概述
自 Spring Boot 1.3 版本起,我們可以利用 EnvironmentPostProcessor 來 在應用程序上下文刷新前自定義應用程序的 Environment。
在本教程中,我們將學習如何將自定義屬性加載並轉換到 Environment 中,然後訪問這些屬性。
2. Spring Environment
Spring 中的 Environment 抽象代表當前應用程序運行的環境。同時,它致力於統一訪問各種屬性源中的方法,例如屬性文件、JVM 系統屬性、系統環境變量和 Servlet 上下文參數。
因此,在大多數情況下,自定義 Environment 意味着在將這些屬性暴露給 Bean 之前對其進行操作。請訪問我們之前關於使用 Spring 操作屬性的文章以瞭解更多信息。
3. 快速示例
讓我們現在構建一個簡單的價格計算應用程序。它將以毛利額或淨利額模式計算價格。系統環境變量將決定採用哪種計算模式。
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)) {
// 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。
只要這些屬性存在於環境中,我們將創建一個新的映射,prefixed。 為了簡潔,我們跳過了rename的內容,但請查看代碼示例以獲取完整實現。 The resulting map has the same values as system,但具有前綴的鍵。
最後,我們將新的PropertySource添加到Environment中。 現在,如果一個 Bean 詢問com.baeldung.environmentpostprocessor.calculation.mode,Environment將會諮詢我們的映射。
順便説一下,EnvironmentPostProcessor的 Javadoc 鼓勵我們實現Ordered接口或使用@Order註解。
而且,這當然只是一個屬性源。Spring Boot 允許我們處理大量的源和格式。
3.2. 在 spring.factories 中註冊
為了在 Spring Boot 啓動過程中調用該實現,我們需要將該類註冊到 META-INF/spring.factories 中:
org.springframework.boot.env.EnvironmentPostProcessor=
com.baeldung.environmentpostprocessor.PriceCalculationEnvironmentPostProcessor3.3. 使用 @Value 註解訪問屬性
讓我們在幾個類中使用它們。在示例中,我們有一個 PriceCalculator 接口,以及兩個實現:GrossPriceCalculator 和 NetPriceCalculator。
在我們的實現中,我們可以簡單地使用 @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
}
}這很棒,因為它與訪問任何其他屬性的方式相同,例如我們在 application.properties 中定義的那些。
3.4. 訪問 Spring Boot 自配置中的屬性
現在,讓我們來看一個複雜的案例,即訪問 Spring Boot 自配置中的前置屬性。
我們將創建一個自配置類,用於讀取這些屬性。該類將根據不同的屬性值初始化並注入應用程序上下文中的 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();
}
}類似於 EnvironmentPostProcessor 實現,自動配置類也需要在 META-INF/spring.factories 中進行註冊:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.baeldung.environmentpostprocessor.autoconfig.PriceCalculationAutoConfig這段代碼之所以有效,是因為在 Spring Boot 自配置生效之前,自定義 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。
mvn spring-boot:run
-Dstart-class=com.baeldung.environmentpostprocessor.PriceCalculationApplication
-Dspring-boot.run.arguments="100,4"5. 結論
綜上所述,EnvironmentPostProcessor 的實現能夠從不同位置加載各種格式的任意文件。 此外,我們還可以對屬性進行任何必要的轉換,以便將其方便地提供給 Environment 用於後續使用。 這種靈活性在將基於 Spring Boot 的應用程序與第三方配置集成時尤其有用。