目录
3.如果我特殊指定了spring.config.location呢?那么最高优先级是谁?
5. 那多个配置文件如何加载的,加载完之后又是如何什么规则排序的?
1. 有如下配置,猜猜项目的最后启动端口是多少呢?
相信各位肯定看到网上的各种关于springboot配置文件加载顺序的文章了,各种加载顺序,优先级从高到底依次是:file://config,file://, classpath://config, classpath://,各种说多个配置文件有相同的key,高优先级的值会覆盖低优先级的值的文章,但是当你真的碰到问题的时候,还是一头懵呢?博主就带领你轻松理清楚sprignboot加载配置文件的顺序。
项目的spring.profiles.active=dev,截图上yml文件后面写了server.port=xxx,不用纠结这是properties文件的写法,不是yml文件的写法,截图是因为空间位置为了方便,大家明白意思即可。
针对上述问题,你知道答案吗?不知道的宝子们,那就跟着博主的步伐继续往下看。
2. 答案:配置文件的加载顺序
我们看到启动端口是8071.即项目的根目录下的file://config/application-dev.yml文件中的配置生效了。
那如果我把file://config/application-dev.yml 中的配置注释掉?接下来启动端口会是哪个呢?
请大家先思考三秒,实在思考不出来的继续跟着博主的步伐往下看。
我们看到其实的启动端口是8075.用的是classpath下的即resources目录下的application-dev.yml文件中的配置。
那我们继续注释掉此文件中的配置?看看接下来会发生什么?
发现其实的启动端口变成了8070,即项目根目录即file://config目录下的application.yml配置文件生效了。
我们可以依次按照此方法挨个验证,再次博主就不一一验证了。直接给出结论。
由此我们可以大概得出结论:
1.带profile这种的配置文件的优先级高于不带profile的文件。比如file://config,和classpath://confgi下面都有application-dev.yml文件,则顺序按照2来。如果只有claspath://config下有application-dev.yml文件,则此文件的优先级最高。
2. 优先级从高到底依次是: file://config,file://,classpath://config,classpath://
file://代表的是项目的根目录下,classpath在springboot项目就是resources目录。
注意一下这个目录:classpath://config 这个只会去找resources的config 自身目录下的application的properties或者yml,yaml文件等,不会读取resources的config子目录下面的文件,比如博主上面的干扰项:这个是不会被读取的。
3.如果我特殊指定了spring.config.location呢?那么最高优先级是谁?
博主在idea启动参数中设置了:
-Dspring.config.location=classpath:config/dev/,那么是不是就只会读取resources/config/dev下的配置文件呢?那么此时的启动端口是否是
8076呢?博主的猜测是8076,那我们一起验证一下吧.
果然与博主所料。那我们可以得出:-Dspring.config.location=classpath:config/dev/,设置了此参数后,此目录下的优先级最高,那原来file://config,file://,classpath://config,classpath://下的那些文件还会被读取吗?
我们debug调式一下发现,指定了-Dspring.config.location=classpath:config/dev/只会读取指定目录下的application相关的文件,其他目录下的目录是不会被读取的。
我们先暂时记住这个结论,在后续的源码解读上会被大家揭晓原理。
4.多个文件有相同的key,具体最终以哪个文件为主呢?
多文件相同的key,假设我们暂时不知道springboot具体如何做的,我们自己可能有2种处理方案:
方案1:在put相同key的,key,value的时候,采用覆盖的方式,高优先级的覆盖低优先级的,属性中只保留高优先级的值。
方案2:在put的时候,不同文件的相同的key的值都保留下面,在根据key获取属性值的时候,把多个文件按照优先级排序好,第一个文件中能获取到此key值的时候就返回,也能达到采用高优先级文件值的目的。
那么带着这个疑问?我们来验证一下,我们在PropertyResolver这个属性解析器类中找到了getProperty(String key)这个方法,
那我们就debug调试一下,验证一下:
我们把项目的file://config目录下的application-dev.yml中端口号放开,同时把classpath://application-dev.yml都端口号也放开,我们发现其实这两个文件中的端口号都存储了。
我们继续看getProperty方法,我们直接取端口号发现值为8071,我们观察数据发现file://config/application-dev.yml的配置文件的端口号是8071,classpath://application-dev.yml文件的端口号是8075.我们发现2个文件的相同的key的值都存储了,只是在读取的时候,遍历排序好的配置文件,读取到第一个包含此key的文件的值,就返回了。所以可以看出采取的是上述的方案二。
通过上述截图我们验证了其实springboot采取的是方案2:在put的时候,不同文件的相同的key的值都保留下面,在根据key获取属性值的时候,把多个文件按照优先级排序好,第一个文件中能获取到此key值的时候就返回,也能达到采用高优先级文件值的目的。
那么配置文件到底何时加载的,又是按照什么规则排序好的?且继续看下文。
5. 那多个配置文件如何加载的,加载完之后又是如何什么规则排序的?
回到springboot的启动方法run方法中,我们发现和环境变量相关的方法是prepareEnvironment,
我们继续跟踪一下此方法。
spring listener的原理,大家自行学习,本文不做深究。我们可以看到和属性文件读取相关的listener是:ConfigFileApplicationListener,最终会调用到addPropertySources方法:
再继续看addPropertySource方法:
继续看load方法:
初始化profile的代码:因为我设置的是dev,我们可以看到这里已经得到了dev.
接下来继续看load方法,在load方法中,getSearchLocations()通过这个方法找到要加载的文件的目录,然后再继续调用load(location, name, profile, filterFactory, consumer),此方法进行加载。
先看 getSearchLocations() 方法,
我们看到如果设置了spring.config.location这个变量,则这加载此目录下application相关的文件。这里就印证了我们刚才标题3中的问题,此设置优先级最高,如果设置了此参数,则只读取此配置对应的目录下的application文件。
如果没有配置此参数,则加载的是
可以看到顺序是:
private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
那和我们理解的顺序怎么不一样的,和我们想象中的正好相反,那我们看一下
asResolvedSet方法:
我们发现他这里反转了:
所以
最后得到的结果和我们上面的结论是一样的,优先级从高到低:
file:.config/, file:./, classpath:/config/ , class:/.
接下来我们看具体的加载文件逻辑:
我们看到spring有2个加载器,一个是properties,一个是yml文件的,会挨个按照要加载的文件目录顺序,去拼接文件后缀,最终判断文件是否存在,存在就进行加载。
这里也就是为啥classpath://config/dev目录下的文件不特殊处理不会被加载,因为只会加载classpath://config自身目录下application相关的文件,是不管子目录的。
加载完之后,继续完addLoadedPropertySources方法。我们看到他是先记在application.yml文件,在加载的带profile的文件,最后再次reverse了,保证了带profile的文件优先级最高。
reverse之后:
至此整个的代码加载部分就讲解完毕了。
6.总结
基于以上的分析,我们再次总结一下,加深一下大家的印象。
1. spring.config.location 此参数设置的目录下的文件优先级最高,设置了此参数,则不再读取file://config.file://,classpath等目录下的文件了。
2.没有设置spring.config.location参数,则加载顺序为:file://config,file://,classpath://config,class:/
3.如果设置了spring.profiles.active参数,则不管哪个目录下的带profile的配置文件的优先级都是最高的。比如file://config目录下没有application-dev.yml,resources/config目录下有此文件,则顺序为:resources/config/applicationdev.yml,file://config/applicaiton/yml,file://application.yml依次类推。如果同时有properties和yml文件,则properties文件优先级高于yml.
其他的不带profile的优先级的顺序参考2的顺序。
4.不管高低优先级的文件,如果具体相同的key,相同的key,value都会被存储下来。最后获取属性值的时候,取的是排好序的属性对象的,第一个有此key的文件的值,从而达到了控制优先级的目的。可以参考标题4中的内容。
码字不易,辛苦给点赞关注加评论额。