在Spring Boot的启动流程中,配置文件的加载工作是由ConfigFileApplicationListener负责执行的。该监听器通过监听容器启动时的特定事件,在恰当的时机触发配置加载流程。通俗地说,当运行环境准备就绪后,它便会立即开始运作。

我们先来看核心代码——ConfigFileApplicationListener同时实现了EnvironmentPostProcessor、SmartApplicationListener和Ordered三个接口。它主要监听两个事件:ApplicationEnvironmentPreparedEvent(应用环境准备事件)和ApplicationPreparedEvent(应用准备事件)。当ApplicationEnvironmentPreparedEvent事件被触发时,它会调用onApplicationEnvironmentPreparedEvent方法;当ApplicationPreparedEvent事件发生时,则会调用onApplicationPreparedEvent方法。
public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
// 从spring.factories中获取所有的EnvironmentPostProcessor
List postProcessors = loadPostProcessors();
// 当前对象也是一个EnvironmentPostProcessor
postProcessors.add(this);
// 排序
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
// 加载配置
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
/**
* 当前EnvironmentPostProcessor的处理逻辑
*/
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
}
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
// new一个loader(在spring.factories文件中找到所有的PropertySourceLoader,
// 如properties,yml文件的Loader[PropertiesPropertySourceLoader,YamlPropertySourceLoader])并load
new Loader(environment, resourceLoader).load();
}
}
为什么选择监听特定事件?因为Spring Boot的配置加载必须在环境就绪之后、Bean初始化之前完成。因此,ApplicationEnvironmentPreparedEvent事件是一个非常自然的切入点。整体代码逻辑相当清晰:首先通过loadPostProcessors()方法从spring.factories文件中获取所有注册的EnvironmentPostProcessor实例;接着把自己也加入列表(因为自身也实现了该接口);然后按照优先级进行排序;最后依次调用每个postProcessEnvironment方法。每个EnvironmentPostProcessor都可以向环境中添加自己的属性源,而ConfigFileApplicationListener在该方法中主要完成两件事:先添加一个RandomValuePropertySource(随机值属性源),再通过内部的Loader加载真正的配置文件。
这里的Loader扮演着关键角色。它会在spring.factories中查找所有可用的PropertySourceLoader实现——例如PropertiesPropertySourceLoader负责解析.properties文件,YamlPropertySourceLoader负责解析.yml文件。随后,Loader会按照约定的路径(如application.yml、application.properties以及profile相关的变体)进行加载,最终将解析出的键值对放入环境对象中。整个加载过程体现了“事件驱动 + SPI机制 + 约定优于配置”的经典设计思路。
有趣的是,ConfigFileApplicationListener本身也会作为一个EnvironmentPostProcessor被调用,因此它的postProcessEnvironment方法是整个配置加载流程中至关重要的一环。如果你仔细阅读Spring Boot的源码,会发现这种设计模式非常常见:一个类同时扮演监听器和执行器的角色,通过事件机制将自己串联到正确的生命周期节点上,从而高效地完成配置加载任务。
