在Spring Boot的源码中,配置文件的加载是在应用程序启动的早期阶段进行的。具体来说,配置文件加载的主要步骤发生在SpringApplication
类的run()
方法中的prepareEnvironment
方法中,真正读取我们的配置文件还是PropertySourceLoader。
本篇博客适合准备看源码,和想了解配置文件加载大体逻辑的同学。
本篇文章主要粘贴了加载配置文件的主要处理逻辑的源码,方便各位同学直接定位关键代码,辅助大家了解配置文件被处理的过程。
以下是Spring Boot源码中加载配置文件的主要步骤:
-
prepareEnvironment()
方法: 应用程序的入口点是SpringApplication
类的run()
,加载配置的方法入口run()
中调用的prepareEnvironment()
方法。初始化environment
对象用于后续存储环境信息,以及后续处理逻辑的入口都在这个方法。 -
ApplicationEnvironmentPreparedEvent
事件: 发布环境准备事件,通过spring事件发布机制去处理环境对象。关键代码listeners.environmentPrepared(bootstrapContext, environment);
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { // Create and configure the environment ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); //本行代码是解析配置的核心代码 listeners.environmentPrepared(bootstrapContext, environment); DefaultPropertiesPropertySource.moveToEnd(environment); configureAdditionalProfiles(environment); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }
这行代码我们继续debug的话会发现,核心逻辑就是发布了一个
ApplicationEnvironmentPreparedEvent
事件,通过debug可以发现有以下7个监听器对该事件做了监听。虽然有7个监听器,从类名上看我肯定是重点关注EnvironmentPostPorcessorApplicationListener
-
EnvironmentPostProcessor
接口:我们继续跟踪EnvironmentPostPorcessorApplicationListener
监听器的主要处理逻辑会发现,该监听器的逻辑主要是调用用EnvironmentPostProcessor
接口。直接上代码EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) { ConfigurableEnvironment environment = event.getEnvironment(); SpringApplication application = event.getSpringApplication(); for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(event.getBootstrapContext())) { postProcessor.postProcessEnvironment(environment, application); } }
可以看到上面代码主要就是遍历EnvironmentPostProcessor接口并执行postProcessEnvironment方法,这个地方会遍历哪些接口,大家就自行打断点吧,关键的处理器为
ConfigDataEnvironmentPostProcessor
这个接口postProcessEnvironment方法实现比较复杂,核心逻辑都在里面,分析到里面的方法我已经不行了。这个地方所遍历接口,也是springboot通过其SPI机制,在初始化SpringApplication时set进去的
-
load()
方法:通过我们不断的debugpostProcessEnvironment
方法,好像找到加载配置文件的主线了,load方法一看名字就是我们重点关注的对象!!!下面是postProcessEnvironment到load的调用栈信息:
看一下load方法吧(下面代码有注释哦!):public ConfigData load(ConfigDataLoaderContext context, StandardConfigDataResource resource) throws IOException, ConfigDataNotFoundException { if (resource.isEmptyDirectory()) { return ConfigData.EMPTY; } ConfigDataResourceNotFoundException.throwIfDoesNotExist(resource, resource.getResource()); StandardConfigDataReference reference = resource.getReference(); Resource originTrackedResource = OriginTrackedResource.of(resource.getResource(), Origin.from(reference.getConfigDataLocation())); String name = String.format("Config resource '%s' via location '%s'", resource, reference.getConfigDataLocation()); //获取properties,yml加载器,加载对应的资源生成propertySources,封装成ConfigData List<PropertySource<?>> propertySources = reference.getPropertySourceLoader().load(name, originTrackedResource); return new ConfigData(propertySources); }
关键代码就暂时分析到这儿,当前中间还有很多的逻辑,目前能力有限暂时到这儿,后续也会对该篇博客持续更新!!!!
总结一下吧
发布ApplicationEnvironmentPreparedEvent
事件进行一系列的处理,EnvironmentPostPorcessorApplicationListener
监听处理器,调用EnvironmentPostProcessor
接口处理环境加载配置,关键的接口实现是ConfigDataEnvironmentPostProcessor
类去处理,最后是读取我们本地资源,根据不同的配置文件找到对应的PropertySourceLoader
去加载我们
的properties,yaml 得到PropertySource,然后最后经过层层的封装,最后还是会放到environment中
问题遗留:配置文件优先级、远程配置如何加载的源码体现,后面再补充