天天看點

Spring Boot # EnvironmentPostProcessor

#EnvironmentPostProcessor簡介

EnvironmentPostProcessor在Spring IOC容器建立之前執行,為我們提供了一個很友善的擴充點。

比如

  • ConfigFileApplicationListener是load配置檔案的
  • HostInfoEnvironmentPostProcessor可以擷取本機的網絡相關的資訊
  • CloudFoundryVcapEnvironmentPostProcessor
  • DevToolsHomePropertiesPostProcessor
  • DevToolsPropertyDefaultsPostProcessor
  • SpringApplicationJsonEnvironmentPostProcessor
  • ServoEnvironmentPostProcessor

一個小問題

前面出過一個問題是,在HostInfoEnvironmentPostProcessor中使用InetUtils擷取本機網卡資訊時,如果是多網卡的情況下,在application配置檔案中配置了spring.cloud.inetutils.preferred-networks的時候,HostInfoEnvironmentPostProcessor中擷取的網卡資訊和Spring Cloud App中的IOC容器啟動之後(這裡的描述不準确,往下看),擷取的資訊不一緻。

具體原因可以參看Spring Cloud Eureka 多網卡配置終極說明​此文的描述。下面我們看一下根本原因,

Spring Boot在啟動過程中,會通過同步事件監聽機制,階段性的發送事件,比如說ApplicationStartEvent,ApplicationEnvironmentPreparedEvent,ApplicationPreparedEvent等,其中ApplicationEnvironmentPreparedEvent事件會被ConfigFileApplicationListener捕獲,捕獲之後會執行如下的方法:

private void onApplicationEnvironmentPreparedEvent(
    ApplicationEnvironmentPreparedEvent event) {
  List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
  postProcessors.add(this);
  AnnotationAwareOrderComparator.sort(postProcessors);
  for (EnvironmentPostProcessor postProcessor : postProcessors) {
    postProcessor.postProcessEnvironment(event.getEnvironment(),
        event.getSpringApplication());
  }
}

List<EnvironmentPostProcessor> loadPostProcessors() {
  return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class,
      getClass().getClassLoader());
}      

可以看到,上面的方法是将spring.factories中配置的EnvironmentPostProcessor接口的實作都找出來。

加上ConfigFileApplicationListener自己,然後按照Order注解指定的值排序,排序之後的順序如下:

  • org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor@6b13d1ff,
  • org.springframework.cloud.client.HostInfoEnvironmentPostProcessor@693c9f97,
  • org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor@4406d3dc,
  • org.springframework.boot.context.config.ConfigFileApplicationListener@757f11f0,
  • org.springframework.cloud.netflix.metrics.ServoEnvironmentPostProcessor@63f2959b,
  • org.springframework.boot.devtools.env.DevToolsHomePropertiesPostProcessor@161b3c33,
  • org.springframework.boot.devtools.env.DevToolsPropertyDefaultsPostProcessor@1b56c940

問題就出在這裡,HostInfoEnvironmentPostProcessor是在ConfigFileApplicationListener執行的,導緻HostInfoEnvironmentPostProcessor在使用InetUtils擷取網卡資訊的時候,上下文環境中沒有application檔案中指定的spring.cloud.inetutils.preferred-networks配置。