述
在日常开发中,有这样一种情况,就是开发环境,测试环境,生产环境,所需要的配置不同,我们通常会使用不同的配置文件来解决这个问题,比如开发环境我们有一个 application-dev.yml
的配置,然后生产环境有一个 application-prod.yml
的配置, 然后再不同的环境中,我们可以通过配置 spring.profiles.active
这个属性,来指定哪个配置文件生效
相关配置
这里先介绍几个常用的 profile 相关的配置
1 | # 指定生效的配置 |
1 | # 默认生效的配置(这里要注意,如果配置了 spring.profiles.active 则 spring.profiles.default 不生效,配置在application配置文件中也不生效) |
1 | # 配置多个配置文件同时生效 |
1 | # 指定profile前缀(默认就是application,这个配置要写在命令行中才会生效) |
源码解析
在上一篇文章中我们了解了属性加载的整体流程, 其中 ConfigFileApplicationListener
这个类实现了 EnvironmentPostProcessor
,然后他的 addPropertySources()
方法中,首先加了一个随机数的属性集,然后就是调用了 Loader.load()
这个方法,具体如图:
这里我们重点关注两个方法:
initializeProfiles()
load()
一个一个看,先进入 initializeProfiles()
这个方法中
initializeProfiles()
getDefaultProfiles()
这里进入 this.environment.getDefaultProfiles()
这个方法,看一下他是怎么获取默认的 profile 的
这里先是用 this.defaultProfiles
和 getReservedDefaultProfiles()
这方法的返回值做了一个对比, 我们看一下这两个东西的值到底是什么
可以看到,这里的 defaultProfiles
其实也是调用的 getReservedDefaultProfiles()
这个方法,如下:
这里就是一个单元素集合,值是 default ,这里判断对比一致之后,找我们配置中有没有 spring.profiles.default
,有的话进入 setDefaultProfiles()
这个方法,进去看一下
所以 getDefaultProfiles()
这个方法做的事情就是,从配置中找我们有没有配置 spring.profiles.default
,有的话用我们自定义配置的,没有的话,返回默认的 default
回到上面的 initializeProfiles()
看一下,只有在 profiles 集合size是1的情况下,才会去找默认的profiles, 也就解释了配置了 spring.profiles.active
之后, spring.profiles.default
不会生效的原因
综上所述,如果我们项目中,什么也没有配置的话, initializeProfiles()
这个方法执行完毕后, profiles
这个集合中应该会有两个属性, 一个最开始添加的 null
,一个是默认的 defaultProfile
对象
load()
回到 Loader.load
方法中
initializeProfiles()
走完之后, profiles
集合不为空,然后进入循环, poll()
方法弹出集合中的第一个元素,第一次循环应该是最先添加的 null
, 然后做一个判断 isDefaultProfile()
是否是默认的 profile, null
显然不是,就会进入下面的 load()
方法,我们看一下这个方法的源码
getSearchLocations()
这里首先调用的是 getSearchLocations()
获取一个路径的集合,点进去看一下
这里的这个默认路径 DEFAULT_SEARCH_LOCATIONS
如下
一共 4 个路径, classpath
的根目录和 classpath
下的 /config
目录,还有文件的根目录和文件目录下的 /config
目录
getSearchNames()
再来看一下 getSearchNames()
的源码
获取到名称之后,再次循环,然后调用同名 load
方法,点进去
这里打了一个断点, 这里有两个 propertySourceLoaders
,然后第二层循环中,会调用他们的 getFileExtensions()
方法,我们分别看一下这两个方法的返回
首先是 PropertiesPropertySourceLoader
然后是 YamlPropertySourceLoader
所以这里 PropertiesPropertySourceLoader
是处理 properties
和 xml
两种格式的文件, YamlPropertySourceLoader
是处理 yml
和 yaml
两种格式的文件
然后最终会调用 loadForFileExtension()
这个方法,点进去看一下
这里就是拼接一下配置文件名,然后最终又是调用了一个 load()
方法, 进去看一下
这里的重点是断点处的这个 loadDocument()
方法,这方法会把我们的配置文件解析出来, 然后转成 Document
对象, 点进去看一下
这里在这个 loader.load
方法打个断点运行看一下
这里就是我们项目中的 application.yml
解析出来的一个 key-value 集合,然后下面调用的了 asDocuments()
方法,点进去
这里做了两件事, 首先是通过 Binder
对象,将配置绑定到对应的类中, 然后创建了 Document
对象, 我们看一下他调用的这个构造是赋了哪些值
这里的 activeProfiles
和 includeProfiles
就是上面在配置文件中解析出来的 spring.profiles.active
和 spring.profiles.include
的值, 最终这个 Document
集合返回之后,会循环然后把他们的 activeProfiles
和 includeProfiles
都添加到 this.profiles
集合中去, 这里就解释了为什么我们在配置文件中设置的 spring.profiles.active
和 spring.profiles.include
也会生效
然后回到最开始的load方法中
这个循环会处理完所有的profile,包括在配置文件中读取出来的 profile ,然后最后又去处理了一次profile是null的情况,主要是防止上面的循环没进, 读取一次 application.yml
或者 application.properties
之后会调用一个 addLoadedPropertySources()
方法, 进去看一下
这里就把所有读取到的属性都加到属性集中去
到这里,配置文件的解析就完成了
总结
回头来画画图总结一下配置文件加载的流程
加载入口
initializeProfiles() 方法流程图
profiles集合处理流程
load() 方法流程图
配置文件读取流程图
addLoadedPropertySources() 方法流程图