前言
EnvironmentPostProcessor是一個Environment后置處理器攒发,是spring中比較重要的一個擴展點篙顺。為什么重要呢?因為這個擴展點胁艰,是在日志模塊之前啟動的底洗。例如根據(jù)這個特性腋么,我們可以在日志啟動之前咕娄,利用EnvironmentPostProcessor接口來實現(xiàn)對日志模塊的準備工作亥揖。
源碼鏈路
以springboot啟動為例,(2.7.3版本圣勒,我回去看了2.1.4版本费变,邏輯沒啥變化)
1. run()方法
// 準備環(huán)境
2. ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 所有的實現(xiàn)了SpringApplicationRunListener的listeners
3. listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment);
// 輪詢所有l(wèi)istener發(fā)送事件
4. listener.environmentPrepared(bootstrapContext, environment);
// 默認的EventPublishingRunListener發(fā)布ApplicationEnvironmentPreparedEvent事件
5. this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
事件發(fā)布完畢,找一下訂閱方圣贸,2.4版本以后是在EnvironmentPostProcessorApplicationListener挚歧, 2.4版本之前是在ConfigFileApplicationListener
流程是一樣的:
1. if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
2. private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
SpringApplication application = event.getSpringApplication();
for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
event.getBootstrapContext())) {
postProcessor.postProcessEnvironment(environment, application);
}
}
3. List<EnvironmentPostProcessor> getEnvironmentPostProcessors(ResourceLoader resourceLoader,
ConfigurableBootstrapContext bootstrapContext) {
ClassLoader classLoader = (resourceLoader != null) ? resourceLoader.getClassLoader() : null;
EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);
return postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, bootstrapContext);
}
4. public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
5. Enumeration urls = classLoader.getResources("META-INF/spring.factories");
總結(jié)從spring.factories文件里找key為org.springframework.boot.env.EnvironmentPostProcessor的類,以spi的方式注入的類吁峻。然后遍歷執(zhí)行postProcessEnvironment()方法滑负。
舉例
apollo
1. 將Apollo配置加載提到初始化日志系統(tǒng)之前(1.2.0+)
從1.2.0版本開始在张,如果希望把日志相關的配置(如`logging.level.root=info`或`logback-spring.xml`中的參數(shù))也放在Apollo管理,那么可以額外配置`apollo.bootstrap.eagerLoad.enabled=true`來使Apollo的加載順序放到日志系統(tǒng)加載之前矮慕,更多信息可以參考[PR 1614](https://github.com/apolloconfig/apollo/pull/1614)帮匾。參考配置示例如下:
# will inject 'application' namespace in bootstrap phase
apollo.bootstrap.enabled = true
# put apollo initialization before logging system initialization
apollo.bootstrap.eagerLoad.enabled=true
上面這一段這是從apollo官方文檔copy過來的,我們看看如何實現(xiàn)的痴鳄。
ApolloApplicationContextInitializer實現(xiàn)了EnvironmentPostProcessor接口瘟斜, 然后在springboot啟動發(fā)布ApplicationEnvironmentPreparedEvent事件的時候,觸發(fā)回調(diào)到ApolloApplicationContextInitializer的postProcessEnvironment()方法
@Override
public void postProcessEnvironment(ConfigurableEnvironment configurableEnvironment, SpringApplication springApplication) {
// should always initialize system properties like app.id in the first place
initializeSystemProperty(configurableEnvironment);
Boolean eagerLoadEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_EAGER_LOAD_ENABLED, Boolean.class, false);
//EnvironmentPostProcessor should not be triggered if you don't want Apollo Loading before Logging System Initialization
if (!eagerLoadEnabled) {
return;
}
Boolean bootstrapEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, Boolean.class, false);
if (bootstrapEnabled) {
initialize(configurableEnvironment);
}
}
代碼通俗易懂痪寻,直接拿配置的apollo.bootstrap.eagerLoad.enabled螺句,如果是true,觸發(fā)apollo初始化工作橡类。也就是說當你需要使用到apollo作為自己的配置中心蛇尚,并且需要在日志模塊之前完成初始化,利用的就是Spring的EnvironmentPostProcessor擴展點猫态。小伙伴們可以舉一反三佣蓉,合理的運用到自己項目中。