购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

5.13 application配置文件加载过程分析

在前文对Spring Boot中各类关于配置相关的内容都做出了相关分析,本节将对Spring Boot中关于配置加载的整体过程进行分析。本节主要讨论application.yml(yaml)和application.properties的加载流程。当Spring Boot项目启动时,第一个入口是org.springframework.boot.SpringApplication#run(java.lang.Class<?>,java.lang.String...)方法,再继续向下深入会进入org.springframework.boot.SpringApplication#prepareEnvironment方法,该方法就是核心入口方法。在prepareEnvironment方法中会执行如下代码:

listeners.environmentPrepared(bootstrapContext,environment)

在这段代码中会触发环境准备事件(ApplicationEnvironmentPreparedEvent),实际调用代码如下:

    public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
           ConfigurableEnvironment environment) {
        this.initialMulticaster.multicastEvent(
                   new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.
application, this.args, environment));
    }

在上述代码中发布了ApplicationEnvironmentPreparedEvent事件,在Spring中处理事件还需要对应的事件处理器,关于事件处理器的寻找会通过getApplicationListeners(event,type)代码进行,getApplicationListeners方法执行结果如图5-5所示。

图5-5 getApplicationListeners方法执行结果

在图5-5中可以发现,ApplicationEnvironmentPreparedEvent事件对应的事件处理器有6个:

(1)EnvironmentPostProcessorApplicationListener;

(2)AnsiOutputApplicationListener;

(3)LoggingApplicationListener;

(4)BackgroundPreinitializer;

(5)DelegatingApplicationListener;

(6)FileEncodingApplicationListener。

在上述6个事件处理器中,主要关注的是第(1)个事件处理器,关于事件的处理代码如下:

    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
           // 应用程序环境准备事件
              onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent)
event);
        }
        if (event instanceof ApplicationPreparedEvent) {
           // 应用程序准备事件
           onApplicationPreparedEvent((ApplicationPreparedEvent) event);
        }
        if (event instanceof ApplicationFailedEvent) {
           // 应用程序失败事件
           onApplicationFailedEvent((ApplicationFailedEvent) event);
        }
    }

此时的事件类型是ApplicationEnvironmentPreparedEvent,会调用onApplicationEnvironment-PreparedEvent方法,在onApplicationEnvironmentPreparedEvent方法中会寻找EnvironmentPost-Processor集合并且遍历所有元素调用postProcessEnvironment方法。在整个处理过程中主要对ConfigDataEnvironmentPostProcessor进行关注,在该对象的处理过程中会进行如下方法调度:

    getConfigDataEnvironment(environment, resourceLoader, additionalProfiles).
processAndApply

跟随上述方法继续向下追踪,会追踪到resolveAndLoad方法(org.springframework.boot.context.config.ConfigDataImporter#resolveAndLoad),具体方法如下:

Map<ConfigDataResolutionResult, ConfigData>
    resolveAndLoad(ConfigDataActivationContext activationContext,
           ConfigDataLocationResolverContext locationResolverContext,
    ConfigDataLoaderContext loaderContext,
           List<ConfigDataLocation> locations) {
        try {
           // 获取 profile
              Profiles profiles = (activationContext != null) ? activationContext.
    getProfiles() :null;
           // 解析
             List<ConfigDataResolutionResult> resolved = resolve(locationResolverContext,
profiles, locations);
           // 加载
           return load(loaderContext, resolved);
        }
        catch (IOException ex) {
              throw new IllegalStateException("IO error on loading imports from " +
locations, ex);
        }
    }

在这段代码中需要关注resolved变量,resolved变量信息如图5-6所示。

图5-6 resolved变量信息

通过图5-6可以发现配置文件已经被解析成功,并且资源对象成功获取。在完成资源对象后需要进行load操作,进入load方法(org.springframework.boot.context.config.Config-DataImporter#load)。在load方法中主要关注的是下面的代码:

try {
   // 实际加载配置数据
   ConfigData loaded = this.loaders.load(loaderContext, resource);
   // 如果数据集不为空,则加入各个数据容器中
   if (loaded != null) {
      this.loaded.add(resource);
      this.loadedLocations.add(location);
      result.put(candidate, loaded);
   }
}
catch (ConfigDataNotFoundException ex) {
   handle(ex, location, resource);
}

在上述代码中会通过ConfigData loaded = this.loaders.load(loaderContext,resource)方法获取配置文件中的数据。本例中loaded数据信息如图5-7所示。

图5-7 loaded数据信息

此时数据已经从配置文件中获取,下面需要关注如何放入环境对象中。接下来解析数据的过程是在org.springframework.boot.context.config.ConfigDataEnvironmentContributors#withProcessedImports方法中进行的,在withProcessedImports方法中具体解析代码如下:

    // 解析数据
    Map<ConfigDataResolutionResult, ConfigData> imported =
    importer.resolveAndLoad(activationContext,locationResolverContext, loaderContext,
imports);
    this.logger.trace(LogMessage.of(() -> getImportedMessage(imported.keySet())));
    // 创建当前贡献者和它的子贡献者,子贡献者是imported中的数据
    ConfigDataEnvironmentContributor contributorAndChildren =
    contributor.withChildren(importPhase, asContributors(imported));
    // 设置result数据值
    result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext,
result.getRoot().withReplacement(contributor, contributorAndChildren));
    // 处理标记+1
    processed++;

上述代码中关于配置文件解析的结果存储在imported变量中,imported数据信息如图5-8所示。

图5-8 imported数据信息

得到imported对象后还需要将其转换成ConfigDataEnvironmentContributor,此时数据结构会发生变化,主要是将数据平铺到各个字段中处理,平铺后的数据信息如图5-9所示。

在转换过程中imported数据会放置到ConfigDataEnvironmentContributor变量中。最后得到contributorAndChildren对象后还需要再进行一次转换,转换成ConfigDataEnvironmentContributors信息,ConfigDataEnvironmentContributors数据信息如图5-10所示。

图5-9 平铺后的数据信息

图5-10 ConfigDataEnvironmentContributors数据信息

在得到上述对象后还会再做一些转换,具体分析在这里就不做展开了。得到环境配置提供者后回到processAndApply(org.springframework.boot.context.config.ConfigDataEnvironment#processAndApply)方法,在processAndApply方法最后会进行applyToEnvironment方法的调度,当该方法调度结束后application配置文件中的数据就会被加入环境对象中,环境对象中的配置信息如图5-11所示。

图5-11 环境对象中的配置信息 hPioVhakzLYNdHogy7snuW2yUsWGliDumhxLLmV7tGTHGLraRa3mh/vF7QXR5+Of

点击中间区域
呼出菜单
上一章
目录
下一章
×