最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Boot 鉤子全集實(shí)戰(zhàn)EnvironmentPostProcessor全解

 更新時(shí)間:2026年01月04日 10:33:41   作者:Web天梯之路  
文章詳細(xì)介紹了SpringBoot中的EnvironmentPostProcessor擴(kuò)展點(diǎn),該點(diǎn)在配置加載階段提供強(qiáng)大的定制化能力,適用于配置中心化、加密解密、動(dòng)態(tài)覆蓋和校驗(yàn)等場景,感興趣的朋友跟隨小編一起看看吧

Spring Boot 鉤子全集實(shí)戰(zhàn)(三):EnvironmentPostProcessor詳解

在上一篇中,我們聚焦了 Spring Boot 啟動(dòng)最早的擴(kuò)展點(diǎn) SpringApplicationRunListener.starting(),解決了啟動(dòng)監(jiān)控、失敗告警等核心問題。今天,我們將深入講解配置加載階段的核心擴(kuò)展點(diǎn)——EnvironmentPostProcessor,它是定制化配置加載、動(dòng)態(tài)配置注入、配置加密解密的 “黃金入口”,也是生產(chǎn)環(huán)境中配置治理的核心工具。

一、什么是EnvironmentPostProcessor?

EnvironmentPostProcessor 是 Spring Boot 提供的配置后置處理擴(kuò)展點(diǎn),在以下時(shí)機(jī)被觸發(fā):

  • Environment 已初始化(但未完全加載所有配置源);
  • 應(yīng)用配置文件(application.yml/properties)已讀取,但未生效;
  • ApplicationContext 尚未創(chuàng)建,但可修改 Environment 中的配置;
  • 執(zhí)行優(yōu)先級(jí)高于 @Configuration、@Value 等配置注入邏輯。

? 核心價(jià)值:在配置最終生效前,對(duì)配置進(jìn)行動(dòng)態(tài)修改、補(bǔ)充、加密解密,實(shí)現(xiàn)配置的統(tǒng)一治理。

生產(chǎn)環(huán)境中,這個(gè)擴(kuò)展點(diǎn)常用于解決 “配置中心化”“配置加密”“多環(huán)境配置動(dòng)態(tài)切換” 等核心問題。

二、場景 1:配置中心拉?。ㄌ娲渲梦募?/h3>

業(yè)務(wù)痛點(diǎn)

生產(chǎn)環(huán)境中,若將配置寫死在 application.yml 中,存在以下問題:

  • 配置與代碼耦合,修改配置需重新打包部署;
  • 多環(huán)境(dev/test/prod)配置管理混亂,易出錯(cuò);
  • 配置缺乏統(tǒng)一管控,泄露風(fēng)險(xiǎn)高。

解決方案

基于 EnvironmentPostProcessor 從配置中心(如 Nacos/Apollo/ 攜程 Apollo)拉取配置,覆蓋本地配置,實(shí)現(xiàn)配置與代碼解耦。

實(shí)現(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)級(jí)配置中心拉取處理器
 */
public class ConfigCenterEnvironmentPostProcessor implements EnvironmentPostProcessor {
    // 模擬配置中心客戶端(生產(chǎn)環(huán)境替換為真實(shí)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)境從配置中心拉取真實(shí)配置
            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. 獲取當(dāng)前激活的環(huán)境(通過啟動(dòng)參數(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)先級(jí)高于本地配置)
        MutablePropertySources propertySources = environment.getPropertySources();
        // 添加到最前面,確保優(yōu)先級(jí)最高
        propertySources.addFirst(new MapPropertySource("configCenterProperties", configMap));
        // 4. 打印加載結(jié)果(生產(chǎn)環(huán)境建議用SLF4J)
        System.out.printf("[配置中心] 成功加載 %d 個(gè)配置項(xiàng),環(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
啟動(dòng)測試

添加啟動(dòng)參數(shù)激活生產(chǎn)環(huán)境:--spring.profiles.active=prod

輸出
[配置中心] 開始拉取 prod 環(huán)境配置
[配置中心] 成功加載 5 個(gè)配置項(xiàng),環(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/個(gè)人/05 java天梯之路/01 源碼/03 每日打卡系列/daily-check-in/springboot鉤子/demo/target/classes started by wangmingfei in /Users/wangmingfei/Documents/個(gè)人/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)價(jià)值
  • 配置與代碼解耦,修改配置無需重新部署;
  • 多環(huán)境配置統(tǒng)一管控,避免配置混亂;
  • 配置中心可實(shí)現(xiàn)配置熱更新、灰度發(fā)布等高級(jí)特性;
  • 配置拉取過程可增加鑒權(quán)、加密,提升安全性。

三、場景 2:配置加密解密(敏感配置防泄露)

業(yè)務(wù)痛點(diǎn)

生產(chǎn)環(huán)境中,數(shù)據(jù)庫密碼、Redis 密碼、接口密鑰等敏感配置若明文存儲(chǔ),存在嚴(yán)重安全風(fēng)險(xiǎn):

  • 配置文件泄露導(dǎo)致敏感信息被盜;
  • 運(yùn)維人員可直接查看明文密碼,不符合安全規(guī)范;
  • 審計(jì)無法追溯密碼使用記錄。

解決方案

基于 EnvironmentPostProcessor 對(duì)加密的配置進(jìn)行解密,敏感配置在配置文件中以密文存儲(chǔ),運(yùn)行時(shí)動(dòng)態(tài)解密。

實(shí)現(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)境從安全存儲(chǔ)中讀取,如KMS/本地加密文件)
    private static final String ENCRYPT_KEY = "prod_key_1234567"; // 實(shí)際使用需16/24/32位
    // 密文前綴標(biāo)識(shí)
    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)配置源,只處理應(yī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("[配置解密] 敏感配置解密完成");
    }
    // 解密單個(gè)配置項(xiàng)
    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)先級(jí)配置源)
                environment.getSystemProperties().put(configKey, plainText);
                System.out.printf("[配置解密] 成功解密配置項(xiàng):%s%n", configKey);
            } catch (Exception e) {
                throw new RuntimeException("配置解密失?。? + configKey, e);
            }
        }
    }
    // AES解密實(shí)現(xiàn)(生產(chǎn)環(huán)境建議使用非對(duì)稱加密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
    # 密文存儲(chǔ),前綴標(biāo)識(shí)需要解密
    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==
輸出

[配置解密] 開始處理敏感配置解密
[配置解密] 成功解密配置項(xiàng):spring.datasource.password
[配置解密] 成功解密配置項(xiàng):redis.password
[配置解密] 成功解密配置項(xiàng):app.api.secret
[配置解密] 敏感配置解密完成

生產(chǎn)價(jià)值
  • 敏感配置密文存儲(chǔ),即使配置文件泄露也無法獲取明文;
  • 解密邏輯集中管控,符合等保合規(guī)要求;
  • 可結(jié)合 KMS(密鑰管理服務(wù))實(shí)現(xiàn)密鑰的安全存儲(chǔ),避免硬編碼;
  • 解密過程可增加審計(jì)日志,追溯敏感配置使用記錄。

四、場景 3:多環(huán)境配置動(dòng)態(tài)覆蓋(解決配置沖突)

業(yè)務(wù)痛點(diǎn)

生產(chǎn)環(huán)境中,多環(huán)境配置常出現(xiàn)以下問題:

  • 測試環(huán)境配置污染生產(chǎn)環(huán)境;
  • 不同環(huán)境的配置優(yōu)先級(jí)混亂;
  • 特殊場景(如灰度發(fā)布)需要臨時(shí)覆蓋配置。

解決方案

基于 EnvironmentPostProcessor 實(shí)現(xiàn)配置的動(dòng)態(tài)覆蓋,根據(jù)環(huán)境、機(jī)器標(biāo)簽等條件動(dòng)態(tài)調(diào)整配置優(yōu)先級(jí)。

實(shí)現(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)境配置動(dòng)態(tài)覆蓋處理器 */
public class EnvConfigOverrideProcessor implements EnvironmentPostProcessor {
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        try {
            // 1. 獲取機(jī)器標(biāo)識(shí)(生產(chǎn)環(huán)境可從機(jī)器標(biāo)簽/ECS元數(shù)據(jù)獲?。?
            String hostName = InetAddress.getLocalHost().getHostName();
            String ip = InetAddress.getLocalHost().getHostAddress();
            System.out.printf("[配置覆蓋] 機(jī)器信息:%s(%s)%n", hostName, ip);
            // 2. 判斷是否為灰度機(jī)器
            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. 動(dòng)態(tài)覆蓋配置
            Map<String, Object> overrideConfig = new HashMap<>();
            if (isProd && isGray) {
                // 灰度機(jī)器使用灰度配置
                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("[配置覆蓋] 灰度機(jī)器,加載灰度配置");
            } else if (isProd) {
                // 生產(chǎn)機(jī)器使用生產(chǎn)配置
                overrideConfig.put("app.gray.mode", "false");
                overrideConfig.put("app.log.level", "INFO");
                System.out.println("[配置覆蓋] 生產(chǎn)機(jī)器,加載生產(chǎn)配置");
            } else {
                // 測試/開發(fā)機(jī)器放寬配置限制
                overrideConfig.put("app.log.level", "DEBUG");
                overrideConfig.put("spring.datasource.hikari.maximum-pool-size", "10");
                System.out.println("[配置覆蓋] 非生產(chǎn)機(jī)器,加載測試配置");
            }
            // 4. 覆蓋配置(優(yōu)先級(jí)最高)
            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("獲取機(jī)器信息失敗", e);
        }
    }
}
配置加載

resources/META-INF/spring.factories 中配置:

org.springframework.boot.env.EnvironmentPostProcessor=\
com.example.demo.envprocessor.EnvConfigOverrideProcessor
輸出(生產(chǎn)機(jī)器)

[配置覆蓋] 機(jī)器信息:xxx(127.0.0.1)
[配置覆蓋] 生產(chǎn)機(jī)器,加載生產(chǎn)配置
[配置覆蓋] 最終生效的數(shù)據(jù)庫地址:jdbc:mysql://prod-mysql:3306/prod_db?useSSL=false
[配置覆蓋] 最終生效的灰度模式:false

生產(chǎn)價(jià)值
  • 基于機(jī)器標(biāo)簽動(dòng)態(tài)調(diào)整配置,實(shí)現(xiàn)灰度發(fā)布、藍(lán)綠部署;
  • 避免測試配置污染生產(chǎn)環(huán)境,配置隔離更徹底;
  • 生產(chǎn)環(huán)境可臨時(shí)覆蓋配置,無需修改配置文件;
  • 配置優(yōu)先級(jí)可精細(xì)化控制,解決配置沖突問題。

五、場景 4:配置校驗(yàn)與補(bǔ)全(提前攔截非法配置)

業(yè)務(wù)痛點(diǎn)

生產(chǎn)環(huán)境中,配置錯(cuò)誤常導(dǎo)致應(yīng)用啟動(dòng)后不可用:

  • 核心配置缺失(如數(shù)據(jù)庫地址為空);
  • 配置格式錯(cuò)誤(如端口號(hào)非數(shù)字);
  • 配置值超出合理范圍(如線程池大小設(shè)置為 0)。

解決方案

基于 EnvironmentPostProcessor 在配置生效前進(jìn)行校驗(yàn),非法配置直接終止啟動(dòng),并給出明確的錯(cuò)誤提示。

實(shí)現(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;
/** * 配置校驗(yàn)與補(bǔ)全處理器 */
public class ConfigValidateProcessor implements EnvironmentPostProcessor {
    // 端口號(hào)正則
    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("[配置校驗(yàn)] 開始校驗(yàn)核心配置");
        // 1. 校驗(yàn)核心配置是否存在
        validateConfigExists(environment, "spring.datasource.url");
        validateConfigExists(environment, "spring.datasource.username");
        validateConfigExists(environment, "spring.datasource.password");
        validateConfigExists(environment, "redis.host");
        // 2. 校驗(yàn)配置格式
        validateConfigFormat(environment, "spring.datasource.url", DB_URL_PATTERN, "數(shù)據(jù)庫URL格式非法");
        validateConfigFormat(environment, "server.port", PORT_PATTERN, "端口號(hào)格式非法");
        // 3. 校驗(yàn)配置值范圍
        validatePortRange(environment, "server.port");
        validateThreadPoolSize(environment, "spring.datasource.hikari.maximum-pool-size");
        // 4. 補(bǔ)全默認(rèn)配置
        supplementDefaultConfig(environment, "server.port", "8080");
        supplementDefaultConfig(environment, "spring.datasource.hikari.minimum-idle", "5");
        System.out.println("[配置校驗(yàn)] 核心配置校驗(yàn)通過,默認(rèn)配置已補(bǔ)全");
    }
    // 校驗(yàn)配置是否存在
    private void validateConfigExists(ConfigurableEnvironment environment, String configKey) {
        String value = environment.getProperty(configKey);
        if (!StringUtils.hasText(value)) {
            throw new IllegalArgumentException("核心配置缺失:" + configKey);
        }
    }
    // 校驗(yàn)配置格式
    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 + ",配置項(xiàng):" + configKey + ",值:" + value);
        }
    }
    // 校驗(yàn)端口號(hào)范圍
    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("端口號(hào)超出范圍(1-65535):" + configKey + "=" + value);
            }
        }
    }
    // 校驗(yàn)線程池大小
    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);
            }
        }
    }
    // 補(bǔ)全默認(rèn)配置
    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("[配置補(bǔ)全] 配置項(xiàng) %s 缺失,使用默認(rèn)值:%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
#    # 密文存儲(chǔ),前綴標(biāo)識(shí)需要解密
#    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==
錯(cuò)誤輸出示例
[配置校驗(yàn)] 開始校驗(yàn)核心配置
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',傳輸: '套接字'' 的目標(biāo)虛擬機(jī)斷開連接
生產(chǎn)價(jià)值
  • 提前攔截非法配置,避免應(yīng)用啟動(dòng)后不可用;
  • 錯(cuò)誤提示明確,縮短配置問題排查時(shí)間;
  • 補(bǔ)全默認(rèn)配置,減少配置文件維護(hù)成本;
  • 統(tǒng)一校驗(yàn)規(guī)則,符合生產(chǎn)環(huán)境配置規(guī)范。

六、總結(jié)

EnvironmentPostProcessor 是 Spring Boot 配置治理的核心擴(kuò)展點(diǎn),它在配置最終生效前提供了強(qiáng)大的定制化能力:

  • 配置中心化:從配置中心拉取配置,解耦配置與代碼;
  • 配置加密:敏感配置密文存儲(chǔ),運(yùn)行時(shí)動(dòng)態(tài)解密;
  • 動(dòng)態(tài)覆蓋:基于環(huán)境 / 機(jī)器標(biāo)簽動(dòng)態(tài)調(diào)整配置;
  • 配置校驗(yàn):提前攔截非法配置,保障應(yīng)用啟動(dòng)成功。

相較于 SpringApplicationRunListenerEnvironmentPostProcessor 更聚焦于配置層面的擴(kuò)展,是構(gòu)建 “配置即代碼”“配置統(tǒng)一管控” 生產(chǎn)級(jí)應(yīng)用的關(guān)鍵工具。

到此這篇關(guān)于Spring Boot 鉤子全集實(shí)戰(zhàn)EnvironmentPostProcessor全解的文章就介紹到這了,更多相關(guān)Spring Boot EnvironmentPostProcessor內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springdoc?openapi使用解決方案

    springdoc?openapi使用解決方案

    SpringDoc注解的使用,它是基于OpenAPI?3和Swagger?3的現(xiàn)代化解決方案,相較于舊版的Swagger2即SpringFox,SpringDoc提供了更簡潔、更直觀的注解方式,這篇文章主要介紹了springdoc?openapi使用,需要的朋友可以參考下
    2024-04-04
  • Java 后端接收Request請(qǐng)求參數(shù)的多種方式匯總

    Java 后端接收Request請(qǐng)求參數(shù)的多種方式匯總

    本文給大家總結(jié)了后端接收Request請(qǐng)求參數(shù)的7種方式,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2023-10-10
  • SpringCloud Alibaba Nacos 整合SpringBoot Admin實(shí)戰(zhàn)

    SpringCloud Alibaba Nacos 整合SpringBoot A

    這篇文章主要介紹了SpringCloud Alibaba Nacos 整合SpringBoot Admin實(shí)戰(zhàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java如何將json字符串與實(shí)體類互相轉(zhuǎn)換

    Java如何將json字符串與實(shí)體類互相轉(zhuǎn)換

    在我們調(diào)用三方平臺(tái)接口時(shí),經(jīng)常需要將我們封裝的實(shí)體類轉(zhuǎn)換為json作為傳參,下面這篇文章主要給大家介紹了關(guān)于Java如何將json字符串與實(shí)體類互相轉(zhuǎn)換的相關(guān)資料,需要的朋友可以參考下
    2023-11-11
  • feign遠(yuǎn)程調(diào)用無法傳遞對(duì)象屬性405的問題

    feign遠(yuǎn)程調(diào)用無法傳遞對(duì)象屬性405的問題

    這篇文章主要介紹了feign遠(yuǎn)程調(diào)用無法傳遞對(duì)象屬性405的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java中@Accessors注解的具體使用

    Java中@Accessors注解的具體使用

    @Accessors是Lombok提供的注解之一,用于自動(dòng)生成getter和setter方法,本文主要介紹了@Accessors注解的常用屬性,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • Java中forward轉(zhuǎn)發(fā)與redirect重定向的區(qū)別

    Java中forward轉(zhuǎn)發(fā)與redirect重定向的區(qū)別

    轉(zhuǎn)發(fā)和重定向都是常用的頁面跳轉(zhuǎn)方式,但在實(shí)現(xiàn)上有一些區(qū)別,本文主要介紹了Java中forward轉(zhuǎn)發(fā)與redirect重定向的區(qū)別,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-11-11
  • Mybatis中特殊SQL的執(zhí)行

    Mybatis中特殊SQL的執(zhí)行

    這篇文章主要介紹了Mybatis中特殊SQL的執(zhí)行,介紹內(nèi)容包括模糊查詢、批量刪除、動(dòng)態(tài)設(shè)置表名、添加功能獲取自增的主鍵等相關(guān)資料,需要的小伙伴可以參考一下
    2022-04-04
  • MyBatis配置數(shù)據(jù)庫連接并實(shí)現(xiàn)交互的操作步驟

    MyBatis配置數(shù)據(jù)庫連接并實(shí)現(xiàn)交互的操作步驟

    這篇文章主要介紹了MyBatis配置數(shù)據(jù)庫連接并實(shí)現(xiàn)交互的操作步驟,本文給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2025-04-04
  • SpringBoot中的Javaconfig代碼示例

    SpringBoot中的Javaconfig代碼示例

    JavaConfig是一種使用Java類替代XML配置文件的方式來定義Spring?Bean的機(jī)制,通過使用`@Configuration`和`@Bean`注解,可以將第三方JAR包中的對(duì)象納入Spring?IOC容器管理,本文介紹SpringBoot中的Javaconfig,感興趣的朋友一起看看吧
    2025-02-02

最新評(píng)論

云霄县| 定南县| 会理县| 木兰县| 常州市| 姜堰市| 桃源县| 武胜县| 桓仁| 常山县| 和平县| 游戏| 邢台市| 清苑县| 岢岚县| 南投市| 喜德县| 桂东县| 雅安市| 五河县| SHOW| 永顺县| 桂平市| 涟源市| 郑州市| 镇江市| 阳朔县| 江永县| 武山县| 迁安市| 赤峰市| 庐江县| 高要市| 宝丰县| 北海市| 游戏| 新野县| 西城区| 四平市| 大渡口区| 卢龙县|