Spring Boot EnvironmentPostProcessor 接口使用與LOG日誌輸出的使用

在項目中我們通常會寫一些插件的東西來做一些自動化配置,例如:讀取環境變量,根據不同的環境設定不同的運行參數配置; 那麼,EnvironmentPostProcessor 就很有必要了解一下了;

這個接口的意義就是:能夠在默認的配置資源加載完成後,暫未使用加載到的配置 來實例化bean,這期間想做點配置信息改變;

下面直接貼上實現:

public class CustomEnvironmentPostProcessor implements Ordered, EnvironmentPostProcessor, SmartApplicationListener {

    /**
     * {@link EnvironmentPostProcessor} 中比較特殊,不能直接用 @Slf4j 進行輸出日誌
     */
    private static final DeferredLog       LOGGER                       = new DeferredLog();

    /**
     * yml資源加載器
     */
    private final YamlPropertySourceLoader loader                       = new YamlPropertySourceLoader();
    /**
     * The default order for the processor.
     */
    public static final int                DEFAULT_ORDER                = ConfigFileApplicationListener.DEFAULT_ORDER;

    private int                            order                        = DEFAULT_ORDER;

    /**
     * 部署環境key
     */
    private static final String            DEPLOY_ENV                   = "DEPLOY_ENV";
    /**
     * 規則中心測試環境配置資源路徑
     */
    private static final String            RULE_CENTER_COFNIG_PATH_TEST = "rulecenter/config/config-test.yml";
    /**
     * 規則中心生產環境配置資源路徑
     */
    private static final String            RULE_CENTER_COFNIG_PATH_PRD  = "rulecenter/config/config-prd.yml";
    /**
     * 規則中心預發配置資源路徑
     */
    private static final String            RULE_CENTER_COFNIG_PATH_PRE  = "rulecenter/config/config-pre.yml";

    /**
     * 當前是否是生產環境
     *
     * @return
     */
    public boolean isPrdEnv(String env) {
        return "PRD".equalsIgnoreCase(env);
    }

    /**
     * 當前是否是測試環境
     *
     * @return
     */
    public boolean isTestEnv(String env) {
        return "TEST".equalsIgnoreCase(env) || "DEV".equalsIgnoreCase(env);
    }

    /**
     * 當前是否是測試環境
     *
     * @return
     */
    public boolean isPreEnv(String env) {
        return "PRE".equalsIgnoreCase(env);
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.boot.env.EnvironmentPostProcessor#
     * postProcessEnvironment(org.springframework.core.env.
     * ConfigurableEnvironment, org.springframework.boot.SpringApplication)
     */
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {

        String env = environment.getProperty(DEPLOY_ENV);
        String configFilePath = null;
        if (isTestEnv(env)) {
            configFilePath = RULE_CENTER_COFNIG_PATH_TEST;
        } else if (isPrdEnv(env)) {
            configFilePath = RULE_CENTER_COFNIG_PATH_PRD;
        } else if (isPreEnv(env)) {
            configFilePath = RULE_CENTER_COFNIG_PATH_PRE;
        } else {
            configFilePath = RULE_CENTER_COFNIG_PATH_TEST;
        }
        Resource path = new ClassPathResource(configFilePath);
        PropertySource<?> propertySource = loadYaml(path);
        environment.getPropertySources().addLast(propertySource);
        LOGGER.info("rule center config  loaded!detail->\r\n" + JSONObject.toJSONString(propertySource));
    }

    /**
     * 加載資源
     * 
     * @param path
     * @return
     */
    private PropertySource<?> loadYaml(Resource path) {
        if (!path.exists()) {
            throw new IllegalArgumentException("Resource " + path + " does not exist");
        }
        try {
            return this.loader.load(RuleCenterConstant.CONFIG_ROOT, path).get(0);
        } catch (IOException ex) {
            throw new IllegalStateException("Failed to load yaml configuration from " + path, ex);
        }
    }

    @Override
    public int getOrder() {
        return order;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationPreparedEvent) {
            LOGGER.replayTo(CustomEnvironmentPostProcessor.class);
        }
    }

    @Override
    public boolean supportsSourceType(Class<?> sourceType) {
        return true;
    }

    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        return ApplicationPreparedEvent.class.isAssignableFrom(eventType);
    }

}

第二步就需要我們在META-INF/spring.factories文件中創建下列條目 

#自動讀取環境配置 這裏是讓EnvironmentPostProcessor 生效
org.springframework.boot.env.EnvironmentPostProcessor=\
xxx.app.config.CustomEnvironmentPostProcessor

# Application Listeners 應用監聽, 用來做日誌輸出,可以參考代碼,我們這裏使用了DeferedLog進行日誌輸出的
org.springframework.context.ApplicationListener = \
xxx.config.CustomEnvironmentPostProcessor

這裏做下解釋,爲什麼在EnvironmentPostProcessor這個接口中需要使用DeferedLog進行日誌輸出,這裏主要是因

springboot 初始化加載日誌通過ApplicationListener完成工作的,springboot通過讀取spring.factories擴展配置文件,加載定義好的ApplicationListener,默認的springboot包下

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
#日誌初始化加載,銷燬監聽器,負責完成日誌初始化以及銷燬工作
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

在配置中我們看到springboot默認配置了LoggingApplicationListener,springboot就是通過此監聽完成日誌的加載,初始化,以及銷燬等工作的。

因此 , 如果我們在實現EnvironmentPostProcessor接口時需要用到延遲日誌輸出的工具類,另外我們也配置了應用監聽器,當整個環境準備好了以後,我們就會replayLog, 日誌就如期而至了。

 

分析的很簡單,大家湊合看吧,今天週末,來公司做了一點日誌的優化,寫了點文章,希望對你有用!

 

今天在來上班的地鐵上想明白一件事情,時間是個具有魔力的東西,你把它放到哪件事情上多點,那麼你肯定能做好那件事情,而且還會越來越出色!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章