在項目中我們通常會寫一些插件的東西來做一些自動化配置,例如:讀取環境變量,根據不同的環境設定不同的運行參數配置; 那麼,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, 日誌就如期而至了。
分析的很簡單,大家湊合看吧,今天週末,來公司做了一點日誌的優化,寫了點文章,希望對你有用!
今天在來上班的地鐵上想明白一件事情,時間是個具有魔力的東西,你把它放到哪件事情上多點,那麼你肯定能做好那件事情,而且還會越來越出色!!!