Spring Boot 鉤子全集實戰(zhàn)EnvironmentPostProcessor全解
Spring Boot 鉤子全集實戰(zhàn)(三):EnvironmentPostProcessor詳解
在上一篇中,我們聚焦了 Spring Boot 啟動最早的擴展點 SpringApplicationRunListener.starting(),解決了啟動監(jiān)控、失敗告警等核心問題。今天,我們將深入講解配置加載階段的核心擴展點——EnvironmentPostProcessor,它是定制化配置加載、動態(tài)配置注入、配置加密解密的 “黃金入口”,也是生產(chǎn)環(huán)境中配置治理的核心工具。
一、什么是EnvironmentPostProcessor?
EnvironmentPostProcessor 是 Spring Boot 提供的配置后置處理擴展點,在以下時機被觸發(fā):
- Environment 已初始化(但未完全加載所有配置源);
- 應用配置文件(application.yml/properties)已讀取,但未生效;
- ApplicationContext 尚未創(chuàng)建,但可修改 Environment 中的配置;
- 執(zhí)行優(yōu)先級高于
@Configuration、@Value等配置注入邏輯。
? 核心價值:在配置最終生效前,對配置進行動態(tài)修改、補充、加密解密,實現(xiàn)配置的統(tǒng)一治理。
生產(chǎn)環(huán)境中,這個擴展點常用于解決 “配置中心化”“配置加密”“多環(huán)境配置動態(tài)切換” 等核心問題。
二、場景 1:配置中心拉取(替代原生配置文件)
業(yè)務痛點
生產(chǎn)環(huán)境中,若將配置寫死在 application.yml 中,存在以下問題:
- 配置與代碼耦合,修改配置需重新打包部署;
- 多環(huán)境(dev/test/prod)配置管理混亂,易出錯;
- 配置缺乏統(tǒng)一管控,泄露風險高。
解決方案
基于 EnvironmentPostProcessor 從配置中心(如 Nacos/Apollo/ 攜程 Apollo)拉取配置,覆蓋本地配置,實現(xiàn)配置與代碼解耦。
實現(xiàn)代碼
package com.example.demo.envprocessor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import java.util.HashMap;
import java.util.Map;
/**
* 生產(chǎn)級配置中心拉取處理器
*/
public class ConfigCenterEnvironmentPostProcessor implements EnvironmentPostProcessor {
// 模擬配置中心客戶端(生產(chǎn)環(huán)境替換為真實Nacos/Apollo客戶端)
private static class ConfigCenterClient {
// 根據(jù)環(huán)境拉取配置
public Map<String, Object> pullConfig(String env) {
Map<String, Object> configMap = new HashMap<>();
// 生產(chǎn)環(huán)境從配置中心拉取真實配置
switch (env) {
case "prod":
configMap.put("spring.datasource.url", "jdbc:mysql://prod-mysql:3306/prod_db?useSSL=false");
configMap.put("spring.datasource.username", "prod_user");
configMap.put("spring.datasource.password", "prod_pass123");
configMap.put("redis.host", "prod-redis:6379");
configMap.put("app.prod.mode", "true");
break;
case "test":
configMap.put("spring.datasource.url", "jdbc:mysql://test-mysql:3306/test_db?useSSL=false");
configMap.put("spring.datasource.username", "test_user");
configMap.put("spring.datasource.password", "test_pass123");
configMap.put("redis.host", "test-redis:6379");
configMap.put("app.prod.mode", "false");
break;
default:
configMap.put("spring.datasource.url", "jdbc:mysql://localhost:3306/dev_db?useSSL=false");
configMap.put("spring.datasource.username", "root");
configMap.put("spring.datasource.password", "root");
configMap.put("redis.host", "localhost:6379");
configMap.put("app.prod.mode", "false");
}
return configMap;
}
}
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
// 1. 獲取當前激活的環(huán)境(通過啟動參數(shù)/系統(tǒng)變量傳遞)
String[] activeProfiles = environment.getActiveProfiles();
String env = activeProfiles.length > 0 ? activeProfiles[0] : "dev";
System.out.printf("[配置中心] 開始拉取 %s 環(huán)境配置%n", env);
// 2. 從配置中心拉取配置
ConfigCenterClient client = new ConfigCenterClient();
Map<String, Object> configMap = client.pullConfig(env);
// 3. 將配置注入Environment(優(yōu)先級高于本地配置)
MutablePropertySources propertySources = environment.getPropertySources();
// 添加到最前面,確保優(yōu)先級最高
propertySources.addFirst(new MapPropertySource("configCenterProperties", configMap));
// 4. 打印加載結果(生產(chǎn)環(huán)境建議用SLF4J)
System.out.printf("[配置中心] 成功加載 %d 個配置項,環(huán)境:%s%n", configMap.size(), env);
configMap.forEach((key, value) -> {
// 密碼脫敏輸出
String displayValue = key.contains("password") ? "******" : String.valueOf(value);
System.out.printf("[配置中心] %s = %s%n", key, displayValue);
});
}
}配置加載
在 resources/META-INF/spring.factories 中配置:
org.springframework.boot.env.EnvironmentPostProcessor=\ com.example.demo.envprocessor.ConfigCenterEnvironmentPostProcessor
啟動測試
添加啟動參數(shù)激活生產(chǎn)環(huán)境:--spring.profiles.active=prod
輸出
[配置中心] 開始拉取 prod 環(huán)境配置 [配置中心] 成功加載 5 個配置項,環(huán)境:prod [配置中心] spring.datasource.username = prod_user [配置中心] spring.datasource.url = jdbc:mysql://prod-mysql:3306/prod_db?useSSL=false [配置中心] redis.host = prod-redis:6379 [配置中心] app.prod.mode = true [配置中心] spring.datasource.password = ****** . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.5.8) 2025-12-11T21:46:23.284+08:00 INFO 10075 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication using Java 21.0.9 with PID 10075 (/Users/wangmingfei/Documents/個人/05 java天梯之路/01 源碼/03 每日打卡系列/daily-check-in/springboot鉤子/demo/target/classes started by wangmingfei in /Users/wangmingfei/Documents/個人/05 java天梯之路/01 源碼/03 每日打卡系列/daily-check-in/springboot鉤子/demo) 2025-12-11T21:46:23.288+08:00 INFO 10075 --- [ main] com.example.demo.DemoApplication : The following 1 profile is active: "prod" 2025-12-11T21:46:23.575+08:00 INFO 10075 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http) 2025-12-11T21:46:23.582+08:00 INFO 10075 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2025-12-11T21:46:23.582+08:00 INFO 10075 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.49] 2025-12-11T21:46:23.600+08:00 INFO 10075 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2025-12-11T21:46:23.600+08:00 INFO 10075 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 298 ms 2025-12-11T21:46:23.739+08:00 INFO 10075 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/' 2025-12-11T21:46:23.743+08:00 INFO 10075 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 9.056 seconds (process running for 9.206)
生產(chǎn)價值
- 配置與代碼解耦,修改配置無需重新部署;
- 多環(huán)境配置統(tǒng)一管控,避免配置混亂;
- 配置中心可實現(xiàn)配置熱更新、灰度發(fā)布等高級特性;
- 配置拉取過程可增加鑒權、加密,提升安全性。
三、場景 2:配置加密解密(敏感配置防泄露)
業(yè)務痛點
生產(chǎn)環(huán)境中,數(shù)據(jù)庫密碼、Redis 密碼、接口密鑰等敏感配置若明文存儲,存在嚴重安全風險:
- 配置文件泄露導致敏感信息被盜;
- 運維人員可直接查看明文密碼,不符合安全規(guī)范;
- 審計無法追溯密碼使用記錄。
解決方案
基于 EnvironmentPostProcessor 對加密的配置進行解密,敏感配置在配置文件中以密文存儲,運行時動態(tài)解密。
實現(xiàn)代碼
package com.example.demo.envprocessor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.util.StringUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/** * 敏感配置解密處理器 */
public class EncryptedConfigEnvironmentPostProcessor implements EnvironmentPostProcessor {
// 加密密鑰(生產(chǎn)環(huán)境從安全存儲中讀取,如KMS/本地加密文件)
private static final String ENCRYPT_KEY = "prod_key_1234567"; // 實際使用需16/24/32位
// 密文前綴標識
private static final String ENCRYPT_PREFIX = "encrypt:";
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
System.out.println("[配置解密] 開始處理敏感配置解密");
// 遍歷所有配置源,解密敏感配置
for (PropertySource<?> propertySource : environment.getPropertySources()) {
// 跳過系統(tǒng)配置源,只處理應用配置
if (propertySource.getName().startsWith("system") || propertySource.getName().startsWith("configCenter")) {
continue;
}
// 解密核心敏感配置
decryptConfig(environment, "spring.datasource.password");
decryptConfig(environment, "redis.password");
decryptConfig(environment, "app.api.secret");
}
System.out.println("[配置解密] 敏感配置解密完成");
}
// 解密單個配置項
private void decryptConfig(ConfigurableEnvironment environment, String configKey) {
String value = environment.getProperty(configKey);
if (StringUtils.hasText(value) && value.startsWith(ENCRYPT_PREFIX)) {
try {
// 截取密文部分
String cipherText = value.substring(ENCRYPT_PREFIX.length());
// 解密
String plainText = decrypt(cipherText, ENCRYPT_KEY);
// 替換為明文(注入到最高優(yōu)先級配置源)
environment.getSystemProperties().put(configKey, plainText);
System.out.printf("[配置解密] 成功解密配置項:%s%n", configKey);
} catch (Exception e) {
throw new RuntimeException("配置解密失?。? + configKey, e);
}
}
}
// AES解密實現(xiàn)(生產(chǎn)環(huán)境建議使用非對稱加密RSA)
private String decrypt(String cipherText, String key) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(cipherText));
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
// 加密方法(用于生成密文配置)
public static String encrypt(String plainText, String key) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedBytes);
}
// 測試生成密文
public static void main(String[] args) throws Exception {
// 生成密文:encrypt:xxxxxx
String password = "prod_pass123";
String cipherText = encrypt(password, ENCRYPT_KEY);
System.out.println("密文配置:encrypt:" + cipherText);
}
}配置加載
在 resources/META-INF/spring.factories 中配置:
org.springframework.boot.env.EnvironmentPostProcessor=\ com.example.demo.envprocessor.EncryptedConfigEnvironmentPostProcessor
配置文件(application.yml)
spring:
datasource:
url: jdbc:mysql://prod-mysql:3306/prod_db?useSSL=false
username: prod_user
# 密文存儲,前綴標識需要解密
password: encrypt:DC76b3+IyNwp+f/1QxPiIA==
redis:
host: prod-redis:6379
password: encrypt:DC76b3+IyNwp+f/1QxPiIA==
app:
api:
secret: encrypt:DC76b3+IyNwp+f/1QxPiIA==輸出
[配置解密] 開始處理敏感配置解密
[配置解密] 成功解密配置項:spring.datasource.password
[配置解密] 成功解密配置項:redis.password
[配置解密] 成功解密配置項:app.api.secret
[配置解密] 敏感配置解密完成
生產(chǎn)價值
- 敏感配置密文存儲,即使配置文件泄露也無法獲取明文;
- 解密邏輯集中管控,符合等保合規(guī)要求;
- 可結合 KMS(密鑰管理服務)實現(xiàn)密鑰的安全存儲,避免硬編碼;
- 解密過程可增加審計日志,追溯敏感配置使用記錄。
四、場景 3:多環(huán)境配置動態(tài)覆蓋(解決配置沖突)
業(yè)務痛點
生產(chǎn)環(huán)境中,多環(huán)境配置常出現(xiàn)以下問題:
- 測試環(huán)境配置污染生產(chǎn)環(huán)境;
- 不同環(huán)境的配置優(yōu)先級混亂;
- 特殊場景(如灰度發(fā)布)需要臨時覆蓋配置。
解決方案
基于 EnvironmentPostProcessor 實現(xiàn)配置的動態(tài)覆蓋,根據(jù)環(huán)境、機器標簽等條件動態(tài)調(diào)整配置優(yōu)先級。
實現(xiàn)代碼
package com.example.demo.envprocessor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
/** * 多環(huán)境配置動態(tài)覆蓋處理器 */
public class EnvConfigOverrideProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
try {
// 1. 獲取機器標識(生產(chǎn)環(huán)境可從機器標簽/ECS元數(shù)據(jù)獲取)
String hostName = InetAddress.getLocalHost().getHostName();
String ip = InetAddress.getLocalHost().getHostAddress();
System.out.printf("[配置覆蓋] 機器信息:%s(%s)%n", hostName, ip);
// 2. 判斷是否為灰度機器
boolean isGray = hostName.contains("gray") || ip.startsWith("192.168.100.");
// 判斷是否為生產(chǎn)環(huán)境
boolean isProd = environment.getActiveProfiles().length > 0 &&
"prod".equals(environment.getActiveProfiles()[0]);
// 3. 動態(tài)覆蓋配置
Map<String, Object> overrideConfig = new HashMap<>();
if (isProd && isGray) {
// 灰度機器使用灰度配置
overrideConfig.put("spring.datasource.url", "jdbc:mysql://gray-mysql:3306/prod_db?useSSL=false");
overrideConfig.put("redis.host", "gray-redis:6379");
overrideConfig.put("app.gray.mode", "true");
System.out.println("[配置覆蓋] 灰度機器,加載灰度配置");
} else if (isProd) {
// 生產(chǎn)機器使用生產(chǎn)配置
overrideConfig.put("app.gray.mode", "false");
overrideConfig.put("app.log.level", "INFO");
System.out.println("[配置覆蓋] 生產(chǎn)機器,加載生產(chǎn)配置");
} else {
// 測試/開發(fā)機器放寬配置限制
overrideConfig.put("app.log.level", "DEBUG");
overrideConfig.put("spring.datasource.hikari.maximum-pool-size", "10");
System.out.println("[配置覆蓋] 非生產(chǎn)機器,加載測試配置");
}
// 4. 覆蓋配置(優(yōu)先級最高)
MutablePropertySources propertySources = environment.getPropertySources();
propertySources.addFirst(new MapPropertySource("dynamicOverrideConfig", overrideConfig));
// 5. 打印最終生效的核心配置
System.out.printf("[配置覆蓋] 最終生效的數(shù)據(jù)庫地址:%s%n",
environment.getProperty("spring.datasource.url"));
System.out.printf("[配置覆蓋] 最終生效的灰度模式:%s%n",
environment.getProperty("app.gray.mode"));
} catch (UnknownHostException e) {
throw new RuntimeException("獲取機器信息失敗", e);
}
}
}配置加載
在 resources/META-INF/spring.factories 中配置:
org.springframework.boot.env.EnvironmentPostProcessor=\ com.example.demo.envprocessor.EnvConfigOverrideProcessor
輸出(生產(chǎn)機器)
[配置覆蓋] 機器信息:xxx(127.0.0.1)
[配置覆蓋] 生產(chǎn)機器,加載生產(chǎn)配置
[配置覆蓋] 最終生效的數(shù)據(jù)庫地址:jdbc:mysql://prod-mysql:3306/prod_db?useSSL=false
[配置覆蓋] 最終生效的灰度模式:false
生產(chǎn)價值
- 基于機器標簽動態(tài)調(diào)整配置,實現(xiàn)灰度發(fā)布、藍綠部署;
- 避免測試配置污染生產(chǎn)環(huán)境,配置隔離更徹底;
- 生產(chǎn)環(huán)境可臨時覆蓋配置,無需修改配置文件;
- 配置優(yōu)先級可精細化控制,解決配置沖突問題。
五、場景 4:配置校驗與補全(提前攔截非法配置)
業(yè)務痛點
生產(chǎn)環(huán)境中,配置錯誤常導致應用啟動后不可用:
- 核心配置缺失(如數(shù)據(jù)庫地址為空);
- 配置格式錯誤(如端口號非數(shù)字);
- 配置值超出合理范圍(如線程池大小設置為 0)。
解決方案
基于 EnvironmentPostProcessor 在配置生效前進行校驗,非法配置直接終止啟動,并給出明確的錯誤提示。
實現(xiàn)代碼
package com.example.demo.envprocessor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.StringUtils;
import java.util.regex.Pattern;
/** * 配置校驗與補全處理器 */
public class ConfigValidateProcessor implements EnvironmentPostProcessor {
// 端口號正則
private static final Pattern PORT_PATTERN = Pattern.compile("^\\d{1,5}$");
// 數(shù)據(jù)庫URL正則
private static final Pattern DB_URL_PATTERN = Pattern.compile("^jdbc:\\w+://.+:\\d+/\\w+.*$");
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
System.out.println("[配置校驗] 開始校驗核心配置");
// 1. 校驗核心配置是否存在
validateConfigExists(environment, "spring.datasource.url");
validateConfigExists(environment, "spring.datasource.username");
validateConfigExists(environment, "spring.datasource.password");
validateConfigExists(environment, "redis.host");
// 2. 校驗配置格式
validateConfigFormat(environment, "spring.datasource.url", DB_URL_PATTERN, "數(shù)據(jù)庫URL格式非法");
validateConfigFormat(environment, "server.port", PORT_PATTERN, "端口號格式非法");
// 3. 校驗配置值范圍
validatePortRange(environment, "server.port");
validateThreadPoolSize(environment, "spring.datasource.hikari.maximum-pool-size");
// 4. 補全默認配置
supplementDefaultConfig(environment, "server.port", "8080");
supplementDefaultConfig(environment, "spring.datasource.hikari.minimum-idle", "5");
System.out.println("[配置校驗] 核心配置校驗通過,默認配置已補全");
}
// 校驗配置是否存在
private void validateConfigExists(ConfigurableEnvironment environment, String configKey) {
String value = environment.getProperty(configKey);
if (!StringUtils.hasText(value)) {
throw new IllegalArgumentException("核心配置缺失:" + configKey);
}
}
// 校驗配置格式
private void validateConfigFormat(ConfigurableEnvironment environment, String configKey,
Pattern pattern, String errorMsg) {
String value = environment.getProperty(configKey);
if (StringUtils.hasText(value) && !pattern.matcher(value).matches()) {
throw new IllegalArgumentException(errorMsg + ",配置項:" + configKey + ",值:" + value);
}
}
// 校驗端口號范圍
private void validatePortRange(ConfigurableEnvironment environment, String configKey) {
String value = environment.getProperty(configKey);
if (StringUtils.hasText(value) && PORT_PATTERN.matcher(value).matches()) {
int port = Integer.parseInt(value);
if (port < 1 || port > 65535) {
throw new IllegalArgumentException("端口號超出范圍(1-65535):" + configKey + "=" + value);
}
}
}
// 校驗線程池大小
private void validateThreadPoolSize(ConfigurableEnvironment environment, String configKey) {
String value = environment.getProperty(configKey);
if (StringUtils.hasText(value)) {
try {
int size = Integer.parseInt(value);
if (size < 1 || size > 100) {
throw new IllegalArgumentException("線程池大小超出合理范圍(1-100):" + configKey + "=" + value);
}
} catch (NumberFormatException e) {
throw new IllegalArgumentException("線程池大小必須為數(shù)字:" + configKey + "=" + value);
}
}
}
// 補全默認配置
private void supplementDefaultConfig(ConfigurableEnvironment environment, String configKey, String defaultValue) {
String value = environment.getProperty(configKey);
if (!StringUtils.hasText(value)) {
environment.getSystemProperties().put(configKey, defaultValue);
System.out.printf("[配置補全] 配置項 %s 缺失,使用默認值:%s%n", configKey, defaultValue);
}
}
}配置加載
在 resources/META-INF/spring.factories 中配置:
org.springframework.boot.env.EnvironmentPostProcessor=\ com.example.demo.envprocessor.ConfigValidateProcessor
配置文件
#spring:
# datasource:
# url: jdbc:mysql://prod-mysql:3306/prod_db?useSSL=false
# username: prod_user
# # 密文存儲,前綴標識需要解密
# password: encrypt:DC76b3+IyNwp+f/1QxPiIA==
redis:
host: prod-redis:6379
password: encrypt:DC76b3+IyNwp+f/1QxPiIA==
app:
api:
secret: encrypt:DC76b3+IyNwp+f/1QxPiIA==錯誤輸出示例
[配置校驗] 開始校驗核心配置
22:15:56.187 [main] ERROR org.springframework.boot.SpringApplication -- Application run failed
java.lang.IllegalArgumentException: 核心配置缺失:spring.datasource.url
at com.example.demo.envprocessor.ConfigValidateProcessor.validateConfigExists(ConfigValidateProcessor.java:47)
at com.example.demo.envprocessor.ConfigValidateProcessor.postProcessEnvironment(ConfigValidateProcessor.java:23)
at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent(EnvironmentPostProcessorApplicationListener.java:132)
at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEvent(EnvironmentPostProcessorApplicationListener.java:115)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:185)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:178)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:156)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:138)
at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136)
at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:81)
at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:64)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118)
at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:112)
at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:63)
at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:353)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:313)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350)
at com.example.demo.DemoApplication.main(DemoApplication.java:11)
已與地址為 ''127.0.0.1:57589',傳輸: '套接字'' 的目標虛擬機斷開連接生產(chǎn)價值
- 提前攔截非法配置,避免應用啟動后不可用;
- 錯誤提示明確,縮短配置問題排查時間;
- 補全默認配置,減少配置文件維護成本;
- 統(tǒng)一校驗規(guī)則,符合生產(chǎn)環(huán)境配置規(guī)范。
六、總結
EnvironmentPostProcessor 是 Spring Boot 配置治理的核心擴展點,它在配置最終生效前提供了強大的定制化能力:
- 配置中心化:從配置中心拉取配置,解耦配置與代碼;
- 配置加密:敏感配置密文存儲,運行時動態(tài)解密;
- 動態(tài)覆蓋:基于環(huán)境 / 機器標簽動態(tài)調(diào)整配置;
- 配置校驗:提前攔截非法配置,保障應用啟動成功。
相較于 SpringApplicationRunListener,EnvironmentPostProcessor 更聚焦于配置層面的擴展,是構建 “配置即代碼”“配置統(tǒng)一管控” 生產(chǎn)級應用的關鍵工具。
到此這篇關于Spring Boot 鉤子全集實戰(zhàn)EnvironmentPostProcessor全解的文章就介紹到這了,更多相關Spring Boot EnvironmentPostProcessor內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java 后端接收Request請求參數(shù)的多種方式匯總
本文給大家總結了后端接收Request請求參數(shù)的7種方式,本文結合實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2023-10-10
SpringCloud Alibaba Nacos 整合SpringBoot A
這篇文章主要介紹了SpringCloud Alibaba Nacos 整合SpringBoot Admin實戰(zhàn),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
Java中forward轉(zhuǎn)發(fā)與redirect重定向的區(qū)別
轉(zhuǎn)發(fā)和重定向都是常用的頁面跳轉(zhuǎn)方式,但在實現(xiàn)上有一些區(qū)別,本文主要介紹了Java中forward轉(zhuǎn)發(fā)與redirect重定向的區(qū)別,具有一定的參考價值,感興趣的可以了解一下2023-11-11
MyBatis配置數(shù)據(jù)庫連接并實現(xiàn)交互的操作步驟
這篇文章主要介紹了MyBatis配置數(shù)據(jù)庫連接并實現(xiàn)交互的操作步驟,本文給大家介紹的非常詳細,感興趣的朋友一起看看吧2025-04-04

