配置文件基本使用
配置文件基本使用
文件类型
properties
即 application.properties
,这里就不多介绍了
yaml
YAML的官方文档,其中语法相关的部分:language-overview
YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言),XML,也是一种标记语言。
非常适合用来做以数据为中心的配置文件,YAML 将来会成为配置文件主流的格式,一定要掌握。
其实,在 Python 中,也有一种文件格式专门用于配置文件,那就是 TOML,具体请看《TOML 配置文件.md》
基本语法
-
key: value;key 和 value 之间除了冒号还有一个空格
-
大小写敏感
-
使用缩进表示层级关系
在 properties 文件中,也可以有层级,不过是用
.
来指向下一个层级 -
缩进不允许使用 tab,只允许空格
-
缩进的空格数不重要,只要相同层级的元素左对齐即可
-
#
表示注释 -
字符串无需加引号,如果要加,
''
会转义特殊字符,""
不会被转义特殊字符双引号会输出特殊字符代表的含义
单引号会将特殊字符转化成字符串,和不使用单引号效果一样
数据类型
字面量 - scalar
单个的、不可再分的值。date、boolean、string、number、null
k: v
对象
键值对的集合。map、hash、set、object
行内写法: k: {k1:v1,k2:v2,k3:v3}
#或
k:
k1: v1
k2: v2
k3: v3
数组
一组按次序排列的值。array、list、queue
行内写法: k: [v1,v2,v3]
#或者
k:
- v1
- v2
- v3
实践
建立相关的 bean
@Data
@Component
@ConfigurationProperties(prefix = "student")
public class Student {
private String userName;
private Boolean boss;
private Date birth;
private Integer age;
private String studentInfo;
private Pet pet;
private String[] interests;
private List<String> animal;
private Map<String, Object> score;
private Set<Double> salarys;
private Map<String, List<Pet>> allPets;
}
此类型中使用到的 Pete
@Data
@NoArgsConstructor
@RequiredArgsConstructor
@AllArgsConstructor
public class Pet {
@NotNull
private String name;
private String breed;
}
yaml 中对应的配置
student:
# 默认,会将属性名从驼峰命名法(camelCased)转化为串式命名法(kebab-cased),举个例子 属性名为userName,在yaml中,通过user-name设置
user-name: zhagnsan
boss: true
# 时间默认用 / 分割
birth: 1993/10/15 21:21:00
age: 22
# student-info: 'line one \n line two'
# student-info: line one \n line two
student-info: "line one \n line two"
interests:
- 抽烟
- 喝酒
- 烫头
# 行内写法
animal: [阿黄, 小黑]
salarys: [15, 45.12, 78979]
# key 为中文的时候,要特别注意,不能直接写,要转义一下
# score: {"[语文]":20, "[数学]":15,"english":30}
score: {Chinese: 20, Math: 15, English: 30}
# 行内写法
# pet: {name: yufei, breed: 边牧}
pet:
name: yufei
breed: 边牧
all-pets:
"[猫]":
- {name: 狸花猫, breed: 狸花猫}
- {name: 橘猫, breed: 橘猫}
- {name: 英短, breed: 英短}
- {name: 波斯猫, breed: 波斯猫}
'[狗]':
- name: 金毛
breed: 金毛
- name: 中华田园犬
breed: 中华田园犬
- name: 博美
breed: 博美
- name: 泰迪
breed: 泰迪
启动测试
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) throws JsonProcessingException {
final ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args);
Student bean = context.getBean(Student.class);
// 字符串化
ObjectMapper Obj = new ObjectMapper();
String jsonStr = Obj.writeValueAsString(bean);
System.out.println(jsonStr);
}
}
输出:
{"userName":"zhagnsan","boss":true,"birth":750691260000,"age":22,"studentInfo":"line one \n line two","pet":{"name":"yufei","breed":"边牧"},"interests":["抽烟","喝酒","烫头"],"animal":["阿黄","小黑"],"score":{"Chinese":20,"Math":15,"English":30},"salarys":[15.0,45.12,78979.0],"allPets":{"猫":[{"name":"狸花猫","breed":"狸花猫"},{"name":"橘猫","breed":"橘猫"},{"name":"英短","breed":"英短"},{"name":"波斯猫","breed":"波斯猫"}],"狗":[{"name":"金毛","breed":"金毛"},{"name":"中华田园犬","breed":"中华田园犬"},{"name":"博美","breed":"博美"},{"name":"泰迪","breed":"泰迪"}]}}
JSON 格式化之后,是这样的
{
"userName": "zhagnsan",
"boss": true,
"birth": 750691260000,
"age": 22,
"studentInfo": "line one \n line two",
"pet": {
"name": "yufei",
"breed": "边牧"
},
"interests": [
"抽烟",
"喝酒",
"烫头"
],
"animal": [
"阿黄",
"小黑"
],
"score": {
"Chinese": 20,
"Math": 15,
"English": 30
},
"salarys": [
15.0,
45.12,
78979.0
],
"allPets": {
"猫": [
{
"name": "狸花猫",
"breed": "狸花猫"
},
{
"name": "橘猫",
"breed": "橘猫"
},
{
"name": "英短",
"breed": "英短"
},
{
"name": "波斯猫",
"breed": "波斯猫"
}
],
"狗": [
{
"name": "金毛",
"breed": "金毛"
},
{
"name": "中华田园犬",
"breed": "中华田园犬"
},
{
"name": "博美",
"breed": "博美"
},
{
"name": "泰迪",
"breed": "泰迪"
}
]
}
}
复杂类型 key
如何在映射中使用复杂的 key?使用问号 ?
官方文档:YAML Ain’t Markup Language (YAML™) revision 1.2.2 中的 2.2. Structures
小节说的很清楚
具体解释:What is a complex mapping key in YAML? - Stack Overflow
添加属性
private Map<List<String>, List<User>> userOrganization;
private Map<Pet, List<User>> petHome;
User 类型的 bean
@Data
@NoArgsConstructor
@RequiredArgsConstructor
public class User {
@NonNull
private String name;
@NonNull
private Integer age;
private Pet pet;
}
编写相应的 yaml
user-organization:
# 虽然提示波浪线,但是实际上没有报错
# 没有成功
? - xiashuo
- dfasda
: - name: 'xiashuo'
age: 21
pet-home:
# 虽然提示波浪线,但是实际上没有报错
# 没有成功
? name: xiashuo
breed: 边牧
: - name: xiashuo
age: 21
- name: dfasdfa
age: 45
在编辑器中,虽然提示波浪线,但是实际上没有报错,IDEA 不支持 ?
的解析吗?
最终对应的这两个属性的解析结果是这样的
感觉 key 的解析缺了一步,此场景不常见,就留到以后再探索吧。TODO
此外,对应的 value 倒是都解析成功了
当 key 为字符串类型的列表的时候,有另一种实现方式
user-organization:
"[xiashuo, dfasda]":
- name: 'xiashuo'
age: 21
对应的解析结果
注意点总结
-
默认,会将属性名从驼峰命名法(camelCased)转化为串式命名法(kebab-cased),举个例子 属性名为
userName
,在 yaml 中,通过user-name
设置 -
时间默认用
/
分割,而不是-
,比如2022/10/10
-
列表和映射(或者对象)有两种写法,一种是行内写法,一种是展开写法
-
表示 map 的时候 key 最好不要写中文,比如把
猫
作为 key 的,如果一定要用,那就对中文进行转义,比如'[猫]'
或者"[猫]"
-
养成习惯,符号后面都要打空格,比如
,
比如:
下面重点说 ''
和 ""
在转义特殊字符上的区别
使用 ""
会转义特殊字符,比如 \n
student-info: "line one \n line two"
在浏览 bean 的属性内容的时候,会换行
但是用 ''
或者压根儿不使用引号的时候
student-info: 'line one \n line two'
或者
student-info: line one \n line two
都不会转义
不常用的语法
---
YAML 使用三个破折号 (---
) 将指令与文档内容分开。如果没有指示,这也可以用来指示文档的开始。三个点 (...
) 在不开始新文档的情况下指示文档的结束,用于通信通道中。
& 和 *
重复的节点 (对象) 首先由一个锚 (用 &
号标记) 标识,然后进行别名 (用星号 *
引用)。举个例子
---
hr:
- Mark McGwire
# 给 Sammy Sosa 起了个别名 SS
- &SS Sammy Sosa
rbi:
# 在这引用SS ,即 Sammy Sosa
- *SS
- Ken Griffey
| 和 >
感觉这个可以用于保存那种比较长,长到会换行的那种文本,比如项目的系统的介绍信息。由此可见,yaml 的能力确实是强,用来做配置文件很合适。
标量 (scalar) 内容可以用块表示法编写,使用文字样式 (由 |
表示) 时,其中所有换行符都是有效的。或者,也可以使用折叠样式 (由 >
表示) 编写换行符,其中每个换行符都被折叠为一个空格,除非换行符以空行或更缩进的行结束。
info0: |
aaaa
bbbb
cccc
info1: >
aaaa
bbbb
cccc
info2: >
aaaa
bbbb
cccc
dddd
eeee
info0,所有的换行符都有效,映射到属性中,就是
info1 中,所有的换行符都折叠成了空格
info2 中,虽然还是使用 >
,但是我们发现,只要缩进跟最外层的缩进不一样,就会换行,其余的行依然是换行折叠成空格
yaml 和 yaml
后缀为 yaml 和 yaml,有啥区别,
答案是没啥区别,使用 Yaml 和 yml 文件扩展名创建的 Yaml 文件,两者在解释和语法上是相同的。那为什么要做区分?因为在旧版本的 windows 中,扩展名限制为 3 个字母,如 yml。现在,没有操作系统级别的强制要求扩展名中有 3 个字母。大多数 yaml 用户使用 .yaml
作为首选。
yaml 相对于 properties 的优势
很明显,yaml 可以根据缩进判断出层级关系,就像代码格式化以后更容易看清楚层次一样,而 properties 就无法做到这一点。
以后写项目的配置信息,优先 yaml
跟 properties 一样
yaml 中配置的值,可以直接通过 @Value("${propertiesName}")
注解注入,但是注意,propertiesName
的值只能是文本,不能是 map 或者集合。
properties 到 yaml 的转化插件:Convert YAML and Properties File - IntelliJ IDEs Plugin | Marketplace
配置提示
我们在 yaml 中编写 SpringBoot 自带的配置的时候,配置项都是有提示的,但是编写我们自己添加的配置(@ConfigurationProperties
注解的类的字段)的的时候,是没有自动提示的,我们希望也能够获得提示。
其实这个时候我们可以注意到,只要是使用了 @ConfigurationProperties
注解的类,IDEA 都会给这样一个提示:
实际上我们根据这个提示配置好 Configuration Annotation Processor
(配置注解处理器),编写 yaml 的时候就会有提示了。
在《SpringBoot 基础篇 -3- 容器功能》中学习
@ConfigurationProperties
注解的时候,在@ConfigurationProperties
注解的官网文档中,在官方文档(Configuration Metadata)里出现过引入这个依赖的内容
具体实现原理,在 《Lombok 常用注解.md》的
Annotation Processing
小节分析过。
根据文档内容提示,操作很简单,在 POM 中引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
在打包 build 的时候排除此依赖,因为这个注解处理只在开发的时候编写 yaml 有用,项目已经打包了就没啥用了,为了减小打包的体积,就排除这个依赖算了
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
注意:在 Spring Initailizr 中也可以选择此依赖,就不要手动添加了,记得添加

不仅是配置项的提示,按住 Ctrl
再鼠标左键点击配置项,可以跳转到此配置项对应的 @ConfigurationProperties
注解的类,这对我们进行源码分析的时候非常方便。
如果引入了 spring-boot-configuration-processor
之后,依然无法自动提示,则需要检查一下 IDEA 的设置,有的时候,下载别的插件或者更改系统设置,会去掉其中的一个文件拓展名,被去掉的文件拓展名将无法进行提示。

而且,配置文件还必须被配置为当前 SpringBoot 应用的配置文件,此时,配置文件的文件图标是:
像这种
就表示此配置文件还没有配置为当前 SpringBoot 应用的配置文件。
配置文件还没有配置为当前 SpringBoot 应用的配置文件的原因可能是因为此配置文件不在
classpath:
下的默认的几个配置文件位置中。添加到其中即可。
总结
yaml 的解析有没有配置文件可以定制一下,TODO?
时间久了,我们可以总结出自己的一套模板 yaml 配置
结合 SpringBoot 自动配置来看 SpringBoot 的配置文件,我们就能明白,SpringBoot 配置文件的意义在于将自动配置类的一部分参数外置(同时这些参数有默认值),在配置文件中自定义这些参数,就可以定制自动配置类的工作。但是自动配置类并没有把自动配置过程中所有的参数外置,只外置了一些常用的,所有并不是所有的配置都可以用配置文件配置,当想配置的内容自动配置类没有外置的时候,那要么修改自动配置类,要么只能手动进行 Java 配置。
自动配置相关知识,请看《SpringBoot 基础篇 -2- 自动配置.md》