一文带你揭密springboot配置文件的加载顺序

目录

1. 有如下配置,猜猜项目的最后启动端口是多少呢?

2. 答案:配置文件的加载顺序

3.如果我特殊指定了spring.config.location呢?那么最高优先级是谁?

4.多个文件有相同的key,具体最终以哪个文件为主呢?

5. 那多个配置文件如何加载的,加载完之后又是如何什么规则排序的?

6.总结

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中的内容。

码字不易,辛苦给点赞关注加评论额。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Eclipse是一个开放源代码的集成开发环境(IDE),可用于Java开发,但也可以用于其他编程语言的开发。在本文中,我将向你展示如何使用Eclipse进行Java开发。 1. 下载Eclipse 首先,你需要从Eclipse官方网站下载Eclipse IDE。下载页面上将提供几个不同的版本,包括Eclipse IDE for Java Developers、Eclipse IDE for JavaScript and Web Developers,以及Eclipse IDE for C/C++ Developers等。选择适合你的版本,然后按照安装向导进行安装。 2. 创建Java项目 一旦你安装了Eclipse,你可以启动它并创建一个新的Java项目。选择“File”->“New”->“Java Project”,然后按照向导创建一个新的Java项目。在创建项目时,你需要指定项目名称、项目类型以及JRE版本等信息。 3. 创建Java类 一旦你创建了一个Java项目,你就可以创建一个Java类。选择你的Java项目,在“src”文件夹上右键单击,然后选择“New”->“Class”。输入类名和选择要继承的类(如果有的话),然后点击“Finish”。 4. 编写Java代码 现在你已经创建了一个Java类,可以开始编写Java代码了。在Eclipse的编辑器中,你可以输入Java代码并保存它。当你保存Java文件时,Eclipse会自动编译你的代码,并在Problems视图中显示任何编译错误。 5. 运行Java程序 一旦你编写了Java代码并保存了它,你可以运行Java程序。右键单击Java文件,然后选择“Run As”->“Java Application”。如果一切顺利,你的Java程序将在控制台中输出结果。 6. 调试Java程序 如果你的Java程序出现了错误或不按预期运行,你可以使用Eclipse的调试器来调试它。在Eclipse的编辑器中,你可以设置断点并启动调试器。当程序执行到断点时,调试器会暂停程序并允许你检查变量、运行代码等。 7. 导入外部JAR包 有时,你可能需要使用外部JAR包来完成你的Java项目。在Eclipse中,你可以简单地将外部JAR包导入到你的项目中。右键单击Java项目,然后选择“Build Path”->“Configure Build Path”。在“Libraries”选项卡上,你可以添加外部JAR包。 总结 在本文中,我们介绍了如何使用Eclipse进行Java开发。我们学习了如何创建Java项目、创建Java类、编写Java代码、运行Java程序、调试Java程序以及导入外部JAR包。Eclipse具有强大的功能,可以大大提高Java开发的效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值