总结SpringBoot配置文件加载顺序

5,717 阅读1分钟
在代码重构的过程中,初始化Kafka的Topics在ConsumerListenter中配置文件过程中,使用了spel语言的方式提前加载字符串数组的topics。因为我将KafkaTopicConfig和KafkaConsumerListener没有分包存在不同的包下面。导致SpringBoot在初始化加载的过程中找不到topics的配置,报错。

Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'topics' in value "#{'${topics}'.split(',')}"
最后找原因,发现是因为SpringBoot在加载的过程中默认使用的是按照初始化启动的类Application(@SpringBootApplication的文件)下的包距离Application类所在的包位置从近到远进行扫描(即包路径文件距离Application类的深度)加载。越离Application约近的加载越早。
从这个问题出发,总结下SpringBoot的配置文件加载顺序。

一、创建个SpringBootDemo项目,存放目录
Application属性文件,按优先级排序,位置高的将覆盖位置低的文件配置.
1. 当前项目目录下的一个/config子目录
2. 当前项目目录
3. 项目的resources即一个classpath下的/config包
4. 项目的resources即classpath根路径(root)


二、验证如上的过程:
加载配置文件的顺序可以通过实验验证:
1~8 个位置 分别定义不同的server 端口号 8001~8008
即可验证结果顺序:
1、如果同一个目录下,有application.yml也有application.properties,默认先读取application.properties。
2、如果同一个配置属性,在多个配置文件都配置了,默认使用第1个读取到的,后面读取的不覆盖前面读取到的。
3、创建SpringBoot项目时,一般的配置文件放置在项目的resources目录下,因为配置文件的修改,通过热部署不用重新启动项目,而热部署的作用范围是classpath下。
我司使用的是apollo配置中心,apollo配置支持热部署。具体的Apollo发布过程如下(apollo原理看github.com/ctripcorp/a…):
a.用户在Portal操作配置发布
b.Portal调用Admin Service的接口操作发布
c.Admin Service发布配置后,发送ReleaseMessage给各个Config Service
d.Config Service收到ReleaseMessage后,通知对应的客户端生效。
我司使用的具体文件配置路径如下:


三、配置文件的生效顺序,会对值进行覆盖
@TestPropertySource 注解
命令行参数
Java系统配置属性(System.getProperties())
操作系统环境变量
只有在random.*里包含的属性会产生一个RandomValuePropertySource
在打包的jar外的应用程序配置文件(application.properties,包含YAML和profile变量)
在打包的jar内的应用程序配置文件(application.properties,包含YAML和profile变量)
在@Configuration类上的@PropertySource注解
默认属性(使用SpringApplication.setDefaultProperties指定)

四、配置随机值
profile.secret={random.value}
profile.number={random.int}
profile.bignumber={random.long}
profile.number.less.than.ten={random.int(10)}
profile.number.in.range=${random.int[1024,65536]}
读取使用注解:@Value(value = "${profile.secret}")
注:如果IDEA出现黄点提示,就是提示配置元数据,可以不配置

五、属性占位符
当application.properties里的值被使用时,它们会被存在的Environment过滤,所以我们能够引用先前定义的值
(比如,系统属性(System.setProperty()设置的属性))
profile.name = www.houxiurong.com
profile.desc = ${profile.name} is a domain name

六、其他配置的介绍
端口配置
server.port=9090
时间格式化
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
时区设置
spring.jackson.time-zone=Asia/Shanghai

总结:

在解决上面遇到的问题时将KafkaConsumerListener文件分包放在比KafkaTopicConfig的深一层就可以。


至于在KafkaConsumerListener文件添加@DependsOn(value = "kafkaTopicConfig"),本人将KafkaTopicConfig和KafkaConsumerListener文件放在同一个目录下没有生效。因此,证明@DependOn没有在SpringBoot下起作用,这个注解是Spring Context中的注解。