前提

这篇文章是《SpringBoot2.x入门》专辑的第4篇文章,使用的SpringBoot版本为2.3.1.RELEASEJDK版本为1.8

主要介绍SpringBoot配置文件一些常用属性、配置文件的加载优先级以及一些和配置相关的注意事项。

关于SpringBoot的配置文件

一个基于标准的引入了SpringBoot组件的Maven项目的结构一般如下:

Root(项目根目录)
- src
- main
- java
- resources # <-- 这个就是资源文件的存放目录
- target
pom.xml

资源文件存放在src/main/resouces目录下,而配置文件本质上也是资源文件,所以项目内的配置文件就存放于该目录下。从SpringBoot的属性源加载器PropertySourceLoader的实现来看,目前支持PropertiesYaml两种配置文件类型。两者各有优势:Yaml的配置属性更灵活,而Properties的配置不容易出错(笔者前公司的技术规范中明确了SpringBoot应用必须使用Properties配置文件,因为运维或者开发同事曾因为生产配置使用了Yaml格式的文件,编辑期间因为空格问题导致了严重的生产故障)。下文会使用Properties配置文件作为示例。

SpringBoot的配置文件使用了profileprofile本身就有剖面、配置文件的含义,下面会把profile作为一个专有名词使用)的概念,可以类比为区分不同环境的标识符,一个SpringBoot应用允许使用多个profile,所以配置文件的格式必须为application-${profile}.文件后缀,例如:

src/main/resources
- application.properties
- application-dev.properties # <-- profile = dev,开发环境配置
- application-test.properties # <-- profile = test,测试环境配置

其中不带profile标识的application.properties,可以理解为主配置文件,也就是SpringBoot的配置文件其实有继承关系,项目启动时,主配置文件无论如何都会优先加载,然后被激活的profile标识的配置文件才会加载,可以通过属性spring.profiles.active指定激活哪一个profile配置文件,如:

# 指定加载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)。

可以通过自动装配org.springframework.core.env.Environment,通过Environment#getActiveProfiles()获取当前激活的profile数组,例如:

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接口作为展示,后面的文章会介绍该接口的使用方式。

常用的基本配置属性

一般情况下会引入spring-boot-starter-web开发web项目,有几个基本配置笔者认为是必须的。主配置文件application.properties中应该标识应用名和默认选用的profile,例如API网关的主配置文件如下:

spring.application.name=api-gateway
spring.profiles.active=dev

此外,主配置中间中应该配置一些不容易变动的属性,例如MybatisMapper扫描路径、模板引擎Freemarker的配置属性等等。而profile标识的配置文件中,应该配置一些跟随环境变化的配置或者经常更变的属性,例如MySQL数据源的配置、Redis的连接配置等等,以便通过spring.profiles.active直接切换不同环境的中间件连接属性或者第三方配置。在Hello World类型的项目中,一般添加server.port指定容器的启动端口,如application-dev.properties的内容如下:

server.port=8081

配置文件加载优先级与属性覆盖

除了主配置文件会优先profile标识的配置文件加载之外,SpringBoot还支持通过文件系统加载配置文件,这些配置文件不一定在项目内(准确来说是项目编译之后打出来的包内),还可以存在于特定的磁盘路径中。这一点可以参考SpringBoot官方文档2.Externalized Configuration一节:

默认的配置文件加载优先级顺序是:

  1. file:./config/(项目部署包所在目录的同级config目录下的application-[profile].[properties,yaml]文件)
  2. file:./config/*/(项目部署包所在目录的同级config目录下的任意子目录中的application-[profile].[properties,yaml]文件)
  3. file:./(项目部署包所在目录的application-[profile].[properties,yaml]文件)
  4. classpath:/config/(项目部署包内类路径下的config目录下的application-[profile].[properties,yaml]文件)
  5. 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/,那么配置文件加载优先级顺序是:

  1. file:./custom-config/(项目部署包所在目录的同级custom-config目录下的application-[profile].[properties,yaml]文件)
  2. classpath:custom-config/(项目部署包内类路径下的custom-config目录下的application-[profile].[properties,yaml]文件)
  3. file:./config/(项目部署包所在目录的同级config目录下的application-[profile].[properties,yaml]文件)
  4. file:./config/*/(项目部署包所在目录的同级config目录下的任意子目录中的application-[profile].[properties,yaml]文件)
  5. file:./(项目部署包所在目录的application-[profile].[properties,yaml]文件)
  6. classpath:/config/(项目部署包内类路径下的config目录下的application-[profile].[properties,yaml]文件)
  7. classpath:/(项目部署包内类路径下的application-[profile].[properties,yaml]文件)

基于这个特性,在不对接配置中心的前提下,可以让运维伙伴在生产服务器上先配置好服务所需的生产环境的配置文件:

# 假设这个是生产服务器的文件路径
/data/apps/api-gateway
- api-gateway.jar
- config
- application-prod.properties

在编写启动脚本的时候只需指定profileprod即可,应用会读取/data/apps/api-gateway/config/application-prod.properties的属性,这样就能避免生产配置或者敏感属性泄漏给开发人员。

这里还有一个比较重要的问题就是:如果在多种路径下的配置文件定义了同一个属性,那么属性会依照一个优先级顺序进行覆盖。因为SpringBoot除了配置文件,还支持命令行、JNDI属性、系统属性等等,如果全部列举会比较复杂,这里按照目前分析过的内容列举这个优先级顺序:

  1. 命令行中的属性参数。
  2. 项目部署包之外的application-profile.[properties,yaml]文件。
  3. 项目部署包内的application-profile.[properties,yaml]文件。
  4. 项目部署包之外的application.[properties,yaml]文件。
  5. 项目部署包内的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亲和性。在主流的IDEEclipseIntelliJ 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),不定期推送笔者原创技术文章(绝不抄袭或者转载):

SpringBoot2.x入门教程:理解配置文件的更多相关文章

  1. SpringBoot2.x入门教程:引入jdbc模块与JdbcTemplate简单使用

    这是公众号<Throwable文摘>发布的第23篇原创文章,收录于专辑<SpringBoot2.x入门>. 前提 这篇文章是<SpringBoot2.x入门>专辑的 ...

  2. WCF入门教程(五)配置文件

    WCF入门教程(五)配置文件 服务协定以及实现写好后,需要将相关服务公布出去,就需要HOST来承载,供客户端来调用. 承载服务有两种方式,一种通过配置文件,一种通过代码进行配置.上一章已经介绍了代码方 ...

  3. emacs 入门教程,菜单汉化,配置文件等杂乱文章

    首先来一发ArchWiki的Emacs简体中文的入门教程 https://wiki.archlinux.org/index.php/Emacs_(%E7%AE%80%E4%BD%93%E4%B8%AD ...

  4. D3.js的v5版本入门教程(第四章)—— 理解Update、Enter、Exit

    D3.js的v5版本入门教程(第四章) Update.Enter.Exit是D3.js中很重要的概念,下面来讲一下它们到底是什么?(当你看完后.你就会知道如果数据集个数和选择集个数不匹配的情况下使用d ...

  5. 21分钟 MySQL 入门教程(转载!!!)

    21分钟 MySQL 入门教程 目录 一.MySQL的相关概念介绍 二.Windows下MySQL的配置 配置步骤 MySQL服务的启动.停止与卸载 三.MySQL脚本的基本组成 四.MySQL中的数 ...

  6. kafka入门教程链接

    http://www.aboutyun.com/forum.php?mod=viewthread&tid=12882 经典入门教程 1.Kafka独特设计在什么地方?2.Kafka如何搭建及创 ...

  7. Laravel 5 系列入门教程(一)【最适合中国人的 Laravel 教程】

    Laravel 5 系列入门教程(一)[最适合中国人的 Laravel 教程] 分享⋅ johnlui⋅ 于 2年前 ⋅ 最后回复由 skys215于 11个月前 ⋅ 17543 阅读   原文发表在 ...

  8. Laravel 4 系列入门教程(一)

    默认条件 本文默认你已经有配置完善的PHP+MySQL运行环境,懂得PHP网站运行的基础知识.跟随本教程走完一遍,你将会得到一个基础的包含登录的简单blog系统,并将学会如何使用一些强大的Larave ...

  9. 【军哥谈CI框架】之入门教程之第二讲:分析CI结构和CI是怎么工作的

    [军哥谈CI框架]之入门教程之第二讲:分析CI结构和CI是怎么工作的   之入门教程之第二讲:分析CI结构和CI是如何工作的大家好!上一节,我们共同部署了一个CI网站,做到这一点非常简单,但是,亲们, ...

随机推荐

  1. Java实现信用卡校验

    当你输入信用卡号码的时候,有没有担心输错了而造成损失呢?其实可以不必这么担心,因为并不是一个随便的信用卡号码都是合法的,它必须通过Luhn算法来验证通过. 该校验的过程: 1.从卡号最后一位数字开始, ...

  2. redis 分布式锁的简单使用

    RedisLock--让 Redis 分布式锁变得简单 目录 1. 项目介绍 2. 快速使用 2.1 引入 maven 坐标 2.2 注册 RedisLock 2.3 使用 3. 参与贡献 4. 联系 ...

  3. java关键字static用法详解

    java中有53个关键字,其中包含2个保留字,这篇文章主要介绍一下static这个关键字. static在java中算是一个比较常见的关键字,有着多种用法,因此很有必要好好地了解一番. 一.定义 st ...

  4. 详解CurrentHashMap之预习篇

    CurrentHashMap的出现时为了解决HashMap的高并发导致OOM的缺陷,并且能够保证高性能读取.那么解读CurrentHashMap需要具备哪些知识的呢? HashMap 解读 Java ...

  5. String 的格式化

    使用场景 用于生成redis等key-value 结构的key的格式化,方便管理 eg: String.format(RedisKeys.PURCHASE_ADD_BABY_LOCK,form.get ...

  6. mysql域名解析引起的远程访问过慢?

    MYSQL远程连接速度慢的解决方法 PHP远程连接MYSQL速度慢,有时远程连接到MYSQL用时4-20秒不等,本地连接MYSQL正常,出现这种问题的主要原因是, 默认安装的MYSQL开启了DNS的反 ...

  7. @bzoj - 3711@ [PA2014]Druzyny

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 体育课上,n个小朋友排成一行(从1到n编号),老师想把他们分成若 ...

  8. Dart Memo for Android Developers

    Dart Memo for Android Developers Dart语言一些语法特点和编程规范. 本文适合: 日常使用Kotlin, 突然想写个Flutter程序的Android程序员. Dar ...

  9. [实战] Flutter 上的内存泄漏监控

    一.前言 Flutter 所使用的 Dart 语言具有垃圾回收机制,有垃圾回收就避免不了会内存泄漏. 在 Android 平台上有个内存泄漏检测工具 LeakCanary, 它可以方便地在 debug ...

  10. 深入理解Java虚拟机学习笔记(三)-----类文件结构/虚拟机类加载机制

    第6章 类文件结构 1. 无关性 各种不同平台的虚拟机与所有平台都统一使用的程序存储格式——字节码(即扩展名为 .class 的文件) 是构成平台无关性的基石. 字节码(即扩展名为 .class 的文 ...