这篇文章是《SpringBoot2.x入门》专辑的第4篇文章,使用的SpringBoot
版本为2.3.1.RELEASE
,JDK
版本为1.8
。java
主要介绍SpringBoot
配置文件一些经常使用属性、配置文件的加载优先级以及一些和配置相关的注意事项。git
一个基于标准的引入了SpringBoot
组件的Maven
项目的结构通常以下:github
Root(项目根目录) - src - main - java - resources # <-- 这个就是资源文件的存放目录 - target pom.xml
资源文件存放在src/main/resouces
目录下,而配置文件本质上也是资源文件,因此项目内的配置文件就存放于该目录下。从SpringBoot
的属性源加载器PropertySourceLoader
的实现来看,目前支持Properties
和Yaml
两种配置文件类型。二者各有优点:Yaml
的配置属性更灵活,而Properties
的配置不容易出错(笔者前公司的技术规范中明确了SpringBoot
应用必须使用Properties
配置文件,由于运维或者开发同事曾由于生产配置使用了Yaml
格式的文件,编辑期间由于空格问题致使了严重的生产故障)。下文会使用Properties
配置文件做为示例。web
SpringBoot
的配置文件使用了profile
(profile
自己就有剖面、配置文件的含义,下面会把profile
做为一个专有名词使用)的概念,能够类比为区分不一样环境的标识符,一个SpringBoot
应用容许使用多个profile
,因此配置文件的格式必须为application-${profile}.文件后缀
,例如:spring
src/main/resources - application.properties - application-dev.properties # <-- profile = dev,开发环境配置 - application-test.properties # <-- profile = test,测试环境配置
其中不带profile
标识的application.properties
,能够理解为主配置文件,也就是SpringBoot
的配置文件其实有继承关系,项目启动时,主配置文件不管如何都会优先加载,而后被激活的profile
标识的配置文件才会加载,能够经过属性spring.profiles.active
指定激活哪个profile
配置文件,如:shell
# 指定加载application-dev.properties spring.profiles.active=dev # 或者同时加载application-dev.properties和application-test.properties spring.profiles.active=dev,test
spring.profiles.active
通常能够在主配置文件application.properties
中指定,获取经过启动命令参数指定(java -jar -Dspring.profiles.active=prod app.jar
或者java -jar app.jar --spring.profiles.active=prod
)。json
能够经过自动装配org.springframework.core.env.Environment
,经过Environment#getActiveProfiles()
获取当前激活的profile
数组,例如:api
import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.core.env.Environment; import java.util.Arrays; @Slf4j @SpringBootApplication public class Ch2Application implements CommandLineRunner { @Autowired private Environment environment; public static void main(String[] args) { SpringApplication.run(Ch2Application.class, args); } @Override public void run(String... args) throws Exception { log.info("Active profiles:{}", Arrays.toString(environment.getActiveProfiles())); } }
运行结果以下:数组
这里用到了CommandLineRunner
接口做为展现,后面的文章会介绍该接口的使用方式。springboot
通常状况下会引入spring-boot-starter-web
开发web
项目,有几个基本配置笔者认为是必须的。主配置文件application.properties
中应该标识应用名和默认选用的profile
,例如API
网关的主配置文件以下:
spring.application.name=api-gateway spring.profiles.active=dev
此外,主配置中间中应该配置一些不容易变更的属性,例如Mybatis
的Mapper
扫描路径、模板引擎Freemarker
的配置属性等等。而profile
标识的配置文件中,应该配置一些跟随环境变化的配置或者常常更变的属性,例如MySQL
数据源的配置、Redis
的链接配置等等,以便经过spring.profiles.active
直接切换不一样环境的中间件链接属性或者第三方配置。在Hello World
类型的项目中,通常添加server.port
指定容器的启动端口,如application-dev.properties
的内容以下:
server.port=8081
除了主配置文件会优先profile
标识的配置文件加载以外,SpringBoot
还支持经过文件系统加载配置文件,这些配置文件不必定在项目内(准确来讲是项目编译以后打出来的包内),还能够存在于特定的磁盘路径中。这一点能够参考SpringBoot
官方文档2.Externalized Configuration
一节:
默认的配置文件加载优先级顺序是:
file:./config/
(项目部署包所在目录的同级config
目录下的application-[profile].[properties,yaml]
文件)file:./config/*/
(项目部署包所在目录的同级config
目录下的任意子目录中的application-[profile].[properties,yaml]
文件)file:./
(项目部署包所在目录的application-[profile].[properties,yaml]
文件)classpath:/config/
(项目部署包内类路径下的config
目录下的application-[profile].[properties,yaml]
文件)classpath:/
(项目部署包内类路径下的application-[profile].[properties,yaml]
文件)眼尖的伙伴可能会发现,在项目中的resources
目录下添加的配置文件的加载优先级是最低的(打包后至关于第5条)。能够经过spring.config.location
属性覆盖上面的顺序,如spring.config.location=classpath:/,classpath:/config/
,通常不建议改变默认的配置顺序,除非有特殊的使用场景。
另外,还能够经过spring.config.additional-location
属性指定额外附加的搜索配置文件的路径,而且优先级比默认的配置顺序要高,假如只配置了spring.config.additional-location=classpath:/custom-config/,file:./custom-config/
,那么配置文件加载优先级顺序是:
file:./custom-config/
(项目部署包所在目录的同级custom-config
目录下的application-[profile].[properties,yaml]
文件)classpath:custom-config/
(项目部署包内类路径下的custom-config
目录下的application-[profile].[properties,yaml]
文件)file:./config/
(项目部署包所在目录的同级config
目录下的application-[profile].[properties,yaml]
文件)file:./config/*/
(项目部署包所在目录的同级config
目录下的任意子目录中的application-[profile].[properties,yaml]
文件)file:./
(项目部署包所在目录的application-[profile].[properties,yaml]
文件)classpath:/config/
(项目部署包内类路径下的config
目录下的application-[profile].[properties,yaml]
文件)classpath:/
(项目部署包内类路径下的application-[profile].[properties,yaml]
文件)基于这个特性,在不对接配置中心的前提下,可让运维伙伴在生产服务器上先配置好服务所需的生产环境的配置文件:
# 假设这个是生产服务器的文件路径 /data/apps/api-gateway - api-gateway.jar - config - application-prod.properties
在编写启动脚本的时候只需指定profile
为prod
便可,应用会读取/data/apps/api-gateway/config/application-prod.properties
的属性,这样就能避免生产配置或者敏感属性泄漏给开发人员。
这里还有一个比较重要的问题就是:若是在多种路径下的配置文件定义了同一个属性,那么属性会依照一个优先级顺序进行覆盖。由于SpringBoot
除了配置文件,还支持命令行、JNDI
属性、系统属性等等,若是所有列举会比较复杂,这里按照目前分析过的内容列举这个优先级顺序:
application-profile.[properties,yaml]
文件。application-profile.[properties,yaml]
文件。application.[properties,yaml]
文件。application.[properties,yaml]
文件。举个例子,假如启动参数中添加--app.author=throwable
,配置文件application.properties
中添加属性app.author=throwable-x
,而配置文件application-dev.properties
中添加属性app.author=throwable-y
,那么使用profile=dev
启动应用的时候,优先获取到的是属性app.author=throwable
:
若是看过SpringBoot属性加载的源码可知,其实属性优先级的思路在设计属性加载模块的时候正好相反,全部的配置文件都会进行解析,构成一个复合的PropertySource,后解析的参数老是在顶层,而后获取属性的时候,老是先从顶层获取。
有时候须要配置自定义属性,会出如今IDE
中会没法识别而"标黄"的场景。这个时候能够应用IDE
亲和性。在主流的IDE
如Eclipse
和IntelliJ IDEA
中,只须要引入SpringBoot
的属性元数据描述文件(spring-configuration-metadata.json
或者additional-spring-configuration-metadata.json
),便可让IDE
识别,提供目录引导跳转的功能,再也不"标黄"。具体的作法是在项目的resources/META-INF
目录中引入属性元数据描述文件,而后编写属性描述便可:
// resources/META-INF/spring-configuration-metadata.json { "properties": [ { "name": "app.author", "type": "java.lang.String", "description": "The author of app." } ] }
spring-configuration-metadata.json
文件的格式能够参考SpringBoot
多个starter
中已经存在的文件,完成这一点,代码洁癖患者或者强迫症患者会感受良好。
这篇文章简单总结了配置文件加载的优先级顺序和配置属性的覆盖优先级顺序,这两点须要彻底掌握,能够自行经过一些例子改变一下配置文件进行熟悉。配置属性覆盖的问题很容易致使生产故障,若是掌握了本节的内容,对于SpringBoot
配置属性方面的问题应该能够快速定位和解决。代码仓库:
(本文完 c-2-d e-a-20200705)
技术公众号《Throwable文摘》(id:throwable-doge),不按期推送笔者原创技术文章(毫不抄袭或者转载):