Spring扩展点之EnvironmentPostProcessor

前言

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扩展点。小伙伴们可以举一反三,合理的运用到自己项目中。

你可能感兴趣的:(Spring扩展点之EnvironmentPostProcessor)