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 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, 日志就如期而至了。

 

分析的很简单,大家凑合看吧,今天周末,来公司做了一点日志的优化,写了点文章,希望对你有用!

 

今天在来上班的地铁上想明白一件事情,时间是个具有魔力的东西,你把它放到哪件事情上多点,那么你肯定能做好那件事情,而且还会越来越出色!!!

你可能感兴趣的:(spring,boot,java)