1.前言
使用SpringBoot框架进行开发的时候,通常会在resources目录下创建一个名为application.yml的配置文件,把需要用到的属性放在application.yml文件中。不知道你有没有想过,为什么配置文件要放在resources目录下,可不可以放在其他目录下面?配置文件名称可不可以使用其它名称?如何指定多环境配置?为什么通过Environment对象可以读取到配置属性?为什么运维在启动脚本中指定了配置文件路径后,项目中的配置文件就不生效了?
伴随着这些问题的困扰,我们需要去探索一下SpringBoot框架是如何加载配置。
2.配置加载
2.1 准备Environment
使用过SpringBoot框架的你应该很清楚,在SpringApplication.run()方法中会创建Environment对象,在创建完Environment对象后会发布ApplicationEnvironmentPreparedEvent事件
2.2 监听ApplicationEnvironmentPreparedEvent事件
既然有事件发布就会有监听的地方,监听类为ConfigFileApplicationListener,实现EnvironmentPostProcessor、SmartApplicationListener接口
在监听事件处理方法中,先从spring.factories文件中加载EnvironmentPostProcessor接口的实现,之后将ConfigFileApplicationListener也放入到Environment处理器中
2.3 获取配置文件路径
配置文件路径的获取,先从spring.config.additional-location属性中获取配置文件路径;接着判断是否指定spring.config.location属性,指定则添加对应的路径,未指定则添加默认的配置路径。此处可以得出一个很重要的结论,那就是如果指定 spring.config.location属性,就不会加载private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/";路径下的配置文件。如果需要指定额外的配置文件路径并加载默认路径下的配置文件,应使用spring.config.additional-location属性而不是spring.config.location属性,默认路径的定义正好解释了resources目录下定义的配置文件会被加载的原因。
2.4 配置文件优先级
配置文件路径获取逻辑中可以看出,如果指定了spring.config.additional-location属性,会先放入该属性对应的路径值
默认配置路径解析后会进行倒序排序,排序后的配置路径为file:./config/,file:./config/*/,file:./,classpath:/config/,classpath:/,最终的配置路径为spring.config.additional-location属性指定的路径,file:./config/,file:./config/*/,file:./,classpath:/config/,classpath:/,由此也就明白了spring.config.additional-location属性指定的路径会覆盖默认路径的原因
2.5 获取配置文件名称
既然配置文件路径可以自定义,那么配置文件名称想想就知道也是可以自定义的。
可以看到如果指定了spring.config.name属性,则加载对应属性值作为配置文件名称,spring.config.name属性可以指定多个配置文件名称,名称之间以逗号的方式进行分隔;如果没有指定spring.config.name属性,则使用默认的名称private static final String DEFAULT_NAMES = "application";,由此也就明白了配置文件名称定义为application的原因
2.6 加载配置
在了解完配置文件路径、配置文件名称处理之后,就可以开始加载指定路径下指定名称的配置文件了
2.6.1 策略模式加载配置
如你所知,常用的配置文件有两种类型,一种以.properties、.xml结尾,一种以yaml、yml结尾,针对不同类型需要不同的加载器进行加载
2.6.2 配置属性构建Map
此处以YamlPropertySourceLoader为例
配置文件经过加载后,会组装成key、value的形式
2.6.3 Map构建PropertySource
PropertySource构建很简单,直接以配置文件名称和配置内容Map进行构造
2.6.4 构建Document
构建Document的目的是设置当前文档对应的环境,用于后续的过滤,通过此部分代码,也可以搞明白一件事情,那就是可以通过spring.profiles来指定环境,通过spring.profiles.active来激活对应的环境。
spring:
profiles:
active: dev
---
spring:
profiles: dev
student:
id: 10002
name: student properties dev
---
spring:
profiles: test
student:
id: 10003
name: student properties test
---
spring:
profiles: product
student:
id: 10004
name: student properties product
2.6.5 过滤Document
在2.6.4中设置了文档对应的环境
如果当前应用没有指定环境,那么就加载对应的文档;如果指定了环境,则会过滤出符合指定环境的文档。例如:当前应用指定了dev环境,那么只会加载定义了
spring:
profiles: dev
的文档或者加载名称为application-dev.yml配置文件
2.7 配置放入Environment
配置加载完成之后,会将配置文件对应的PropertySource加入到Environment对象的propertySources属性中,因此我们可以通过Environment对象来获取对应的配置属性。
到此,所有的问题都有了答案,困惑的你也会醍醐灌顶,心情变得舒畅了,人也变得更加自信了。