文章目录
- 一、前言
- 二、SpringBoot配置文件目录读取顺序
- 源码解析
- 三、SpringBoot配置文件类型读取顺序
- 源码解析
一、前言
本文通过源码分析 SpringBoot 加载配置文件目录的顺序,以及 properties、xml、yml、yaml文件的读取顺序
二、SpringBoot配置文件目录读取顺序
配置文件目录读取顺序(由高到低):
- file:./config/ 当前项目下的/config目录
- file:./ 当前项目的根目录
- classpath:/config/ classpath的/config目录
- classpath:/ classpath的根目录
源码解析
我们从 SpringApplication.run
开始,一直往里走,来到 run(java.lang.String...)
方法,如下
加载配置文件属于准备环境,我们继续跟进 prepareEnvironment
方法
这里在环境准备就绪之后,会触发 environmentPrepared
事件,我们跟进 listeners.environmentPrepared(environment);
方法
这里循环了所有的 SpringApplicationRunListener
调用其 environmentPrepared
方法,这个 listeners
其实就是读取 META-INF
目录下的 spring.factories
文件里的 org.springframework.boot.SpringApplicationRunListener
,见下图
ps:这里其实也是一个扩展点,如果你需要在环境准备就绪后,做一些自定义的操作,就可以自己写个类实现
SpringApplicationRunListener
接口,然后在自己的项目/META-INF
目录下创建一个名为spring.factories
的文件,在文件中添加org.springframework.boot.SpringApplicationRunListener=自定义类的全路径
扯回来,我们知道这里的 listeners
其实就是 EventPublishingRunListener
(见图),继续跟进其 environmentPrepared
方法
这里调用了 Spring 的事件广播器去做事件广播,注意这里事件类是 ApplicationEnvironmentPreparedEvent
后面有用,继续跟进 multicastEvent
方法
这里就是 Spring 事件广播的标准写法了,根据 event
找到匹配的 ApplicationListener
,调用 invokeListener
方法,进去再调用 doInvokeListener
方法,最后会调用到 ApplicationListener
的 onApplicationEvent
方法,具体怎么找到这些 ApplicationListener
的,这里就不展开了,在众多匹配的 ApplicationListener
里有一个 ConfigFileApplicationListener
,看名字就知道,专门用来处理配置文件的,我们看它的 onApplicationEvent
方法
上面我们知道事件类是 ApplicationEnvironmentPreparedEvent
,所以这里走的是 onApplicationEnvironmentPreparedEvent
方法,继续跟进
这里又从 spring.factories
文件里获取了 org.springframework.boot.env.EnvironmentPostProcessor
,并且把自己(ConfigFileApplicationListener
)也算进去了,因为 ConfigFileApplicationListener
也实现了 EnvironmentPostProcessor
接口,然后执行了这些 EnvironmentPostProcessor
的 postProcessEnvironment
方法,我们这里还是重点看 ConfigFileApplicationListener
的 postProcessEnvironment
方法
继续跟进 addPropertySources
方法
继续跟进 load
方法
这个方法里 initializeProfiles
方法会根据 spring.profiles.active
配置来决定加载哪个配置文件,如果没有就用 spring.profiles.default
配置,如果配的是 dev
,就加载 application-dev.properties
或 application-dev.yml
这样的配置文件,如果这两个参数都没有配,就加载 application.properties
或 application.yml
这样的配置文件,下面具体看加载的逻辑里是如何获取目录的,跟进 load
方法
很明显了,获取扫描目录的代码在 getSearchLocations
方法里
可以看到返回值正是这4个目录,这个值一般都是取自 DEFAULT_SEARCH_LOCATIONS
这个常量,我们看下
这里需要注意,这个常量是按优先级从低到高倒序排列的。
三、SpringBoot配置文件类型读取顺序
配置文件类型读取顺序(由高到低):
- properties
- xml
- yml
- yaml
源码解析
在上面我们讲到获取目录,下面就是要循环每个目录,在每个目录下找配置文件了,那么配置文件类型有好几种,读取的先后顺序是怎样的呢,我们先找到 load
方法
可以看到循环中又调用了 load
方法,跟进
可以看到走进了一个双层嵌套循环,循环调用了 loadForFileExtension
方法,方法里就是具体加载文件的源码了,我们这里重点关注的是文件类型的读取顺序,就不深究加载文件的源码了,我们看这个双层嵌套的循环,分别循环的是什么,先是循环的 propertySourceLoaders
,然后再循环 loader 的 getFileExtensions
方法返回的 String 数组,这个 propertySourceLoaders
是什么呢,我们发现又又是读取的 spring.factories
文件,这回读的是 org.springframework.boot.env.PropertySourceLoader
,在文件的最上面
可以看到先后顺序是 PropertiesPropertySourceLoader
,YamlPropertySourceLoader
,我们再分别看这两个 loader 的 getFileExtensions
方法
结论很明显了,顺序是 properties > xml > yml > yaml。