前言
EnvironmentPostProcessor是一个Environment后置处理器,是spring中比较重要的一个扩展点。为什么重要呢?因为这个扩展点,是在日志模块之前启动的。例如根据这个特性,我们可以在日志启动之前,利用EnvironmentPostProcessor接口来实现对日志模块的准备工作。
源码链路
以springboot启动为例,(2.7.3版本,我回去看了2.1.4版本,逻辑没啥变化)
1. run()方法
// 准备环境
2. ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 所有的实现了SpringApplicationRunListener的listeners
3. listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment);
// 轮询所有listener发送事件
4. listener.environmentPrepared(bootstrapContext, environment);
// 默认的EventPublishingRunListener发布ApplicationEnvironmentPreparedEvent事件
5. this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
事件发布完毕,找一下订阅方,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 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 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");
总结从spring.factories文件里找key为org.springframework.boot.env.EnvironmentPostProcessor的类,以spi的方式注入的类。然后遍历执行postProcessEnvironment()方法。
举例
apollo
1. 将Apollo配置加载提到初始化日志系统之前(1.2.0+)
从1.2.0版本开始,如果希望把日志相关的配置(如`logging.level.root=info`或`logback-spring.xml`中的参数)也放在Apollo管理,那么可以额外配置`apollo.bootstrap.eagerLoad.enabled=true`来使Apollo的加载顺序放到日志系统加载之前,更多信息可以参考[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过来的,我们看看如何实现的。
ApolloApplicationContextInitializer实现了EnvironmentPostProcessor接口, 然后在springboot启动发布ApplicationEnvironmentPreparedEvent事件的时候,触发回调到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,触发apollo初始化工作。也就是说当你需要使用到apollo作为自己的配置中心,并且需要在日志模块之前完成初始化,利用的就是Spring的EnvironmentPostProcessor扩展点。小伙伴们可以举一反三,合理的运用到自己项目中。