SpringBoot项目不同于SSM框架繁多的配置文件,生成项目时若没有特殊需要,一般只需要一个配置文件,默认在项目根目录下生成一个application.properties文件,但官方更加推荐我们使用另一格式,也就是yml格式的文件。
配置文件的作用
之前有说到,SpringBoot的自动装配已经帮我们默认配置好了许多参数,但有时候我们需要自定义一些参数,不想使用默认的,在配置文件中重新填写即可。例如,SpringBoot默认配置项目的运行端口号为8080,在配置文件中我们可以修改:
server:
port: 8081
此处使用的便是yaml写法,接下来看看什么是yaml语法
YAML语法简介
yml是YAML(YAML Ain’t Markup Language)语言的文件,以数据为中心,比properties、xml等更适合做配置文件。
与properties文件语法不同,yaml语法使用的是K: V的方式,对空格极其敏感,以属性name为例,下面看看两者的不同:
name=fengjian
port: 8081
再看看与xml文件相比,yml体现出来的简洁性:
<server>
<port>8081<port>
server>
server:
prot: 8080
基础语法
例如一名学生的姓名和年龄:
student:
name: fengjian
age: 18
姓名与年龄归属于学生这一对象下的同级属性,两者对齐代表同一级,空格代表属于student下的一级
数据类型
student:
name: fengjian # 字符串
age: 18 # 数字
graduated: false # Boolean
birth: 2021/01/01 10:20:12 # 时间
skill: # list
-1
-2
subject: [set1,set2] # set
family: {key1: value1,key2: value2} # map
注意:
同样可以使用行内写法:
student: {name: fengjian,age: 3,birth: 2021/01/01 10:20:12}
与其他配置文件不同,yml文件可以直接将其中的值注入到类中。首先在项目根目录下编写一个application.yml文件
注入实体类
编写一个实体类Dog,并写好构造器、getset方法,并将其注入到Spring容器中:
@Component //注入到Spring容器中
public class Dog {
private String firstName;
private Integer age;
}
利用spring中的注解@Value,在属性上方也能对他们进行赋值:
@Value("旺财")
private String firstName;
@Value("1")
private Integer age;
public Dog() {
}
而在使用了yaml后,可以直接从配置文件中读取值,此时再新建一个person类:
@Component
public class Person {
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String,Object> maps;
private List<Object> list;
private Dog dog;
}
在yml文件中编写对应的person对象:
person:
name: fengjian
age: 18
happy: false
birth: 2021/7/17
maps: {k1: v1,k2: v2}
hello: happy
list:
- program
- music
dog:
name: 旺财
age: 3
在实体类中加入@ConfigurationProperties注解,将类与配置文件关联起来:
@Component
@ConfigurationProperties(prefix = "person")
/*
@ConfigurationProperties作用:
将配置文件中配置的每一个属性的值,映射到这个组件中;
告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定
参数 prefix = “person” : 将配置文件中的person下面的所有属性一一对应
*/
public class Person {
}
加载指定的配置文件
在上述例子中,我们使用@ConfigurationProperties(prefix="")将实体类与配置文件中指定的对象绑定,实现了从全局配置文件(application.yml)读取值。但有时我们希望全局配置文件中只配置一些项目相关的参数,其他参数放在其他配置文件中,那么此时就需要通过加载指定的配置文件来完成了,我们需要用到另一个注解@PropertySource 。
我们新建一个application.properties文件,注意先检查一下.properties文件的编码格式:
接着在里面放一个变量name:
此时我想将name赋值到Person类的name中,那么在Person类的头部加上@PropertySource注解:
@PropertySource(value = "classpath:/application.properties")
@Component
public class Person {
@Value("${name}")
private String name;
}
注意使用@PropertySource注解时,引入的是目标配置文件的全路径。在测试类中运行它:
可以看到,因为我只给person的name复制了,其他的属性应该为空,说明运行成功
同样,在配置文件中,我们还可以使用E-L表达式${}引入系统变量,比如使用随机数:
r a n d o m . i n t , 或 是 引 用 当 前 配 置 文 件 中 上 文 中 的 某 个 变 量 : {random.int},或是引用当前配置文件中上文中的某个变量: random.int,或是引用当前配置文件中上文中的某个变量:{person.hello},直接看这个例子:
person:
name: fengjian${random.int} # 使用随机数
age: ${random.int}
happy: false
birth: 2021/7/17
maps: {k1: v1,k2: v2}
hello: happy
list:
- program
- music
dog:
firstName: ${person.hello:hello}_旺财 #引用上文中person中的hello属性性,如果存在则使用该变量,如果不存在则显示hello
age: 3
这里拿yaml与properties进行比较,在上述两种赋值方式中可以发现,因为yaml可以存储对象而properties只能存储键值对,这就导致了在我们需要将多个值赋给实体类中时,properties需要一个个去赋值,而yaml只需一次绑定即可完成。
接下来用一张表归纳出两者的区别:
@ConfigurationProperties | @Value | |
---|---|---|
功能 | 批量注入配置文件中的属性 | 挨个指定 |
松散绑定(松散语法) | 支持 | 不支持 |
SpEL | 不支持 | 支持 |
JSR303数据校验 | 支持 | 不支持 |
复杂类型封装 | 支持 | 不支持 |
JSR是Java Specification Requests的缩写,意思是Java 规范提案。而JSR-303(Bean Validation 1.0 (JSR 303))是Java用于企业级开发中对Bean进行校验规范的技术。在上述的表中提到了,@ConfigurationProperties注解支持JSR-303数据校验,以及支持松散绑定,接下来将通过案例挨个介绍它们。
松散绑定
在类命名一个属性我们一般采用的是驼峰命名法,而在yml配置文件中,我们绑定类中对应的属性并不需要与之相同,比如类中的firstName,在yml中我们不仅可以写成first-name,还可以写成first_name,都是能够绑定成功的。
JSR-303
首先在实体类的上方使用@Validated注解开启校验,注意新版本的SpringBoot需要引入spring-boot-starter-validation依赖才可以使用它:
以上面的实体类Person为例,假设想让name字段固定为电子邮件格式,只需在该属性上添加注解@Email:
注意看清楚,该注解是位于javax.Validation下的。该注解中有一个变量message,用于输出验证格式错误时的信息:
可以看到是有默认值的,我们也可以自己写一条信息:
@Email(message = "格式错误")
private String name;
绑定配置文件中的Person不变:
在测试类中输出:
发现报错了:
以上是JSR-303数据校验中的一个小例子,当然它能做的数据校验远远不止这些,下面列出一些常用的注解:
Constraint | 详细信息 |
---|---|
@Null | 被注释的元素必须为 null |
@NotNull | 被注释的元素必须不为 null |
@AssertTrue | 被注释的元素必须为 true |
@AssertFalse | 被注释的元素必须为 false |
@Min(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Max(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@DecimalMin(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@DecimalMax(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@Size(max, min) | 被注释的元素的大小必须在指定的范围内 |
@Digits (integer, fraction) | 被注释的元素必须是一个数字,其值必须在可接受的范围内 |
@Past | 被注释的元素必须是一个过去的日期 |
@Future | 被注释的元素必须是一个将来的日期 |
@Pattern(value) | 被注释的元素必须符合指定的正则表达式 |
被注释的元素必须是电子邮箱地址 | |
@Length | 被注释的字符串的大小必须在指定的范围内 |
@NotEmpty | 被注释的字符串的必须非空 |
@Range | 被注释的元素必须在合适的范围内 |
实际上这些注解都位于上面我提到的javax.Validation包中,而该包位于名为jakarta.validation-api.jar中:
在实际开发过程中,我们在不同的环境中可能会用到不同的配置文件,如生产环境、测试环境以及项目上线后的运行环境等等。这时候我们就要用到多配置文件间的切换来达到切换环境的目的了。
再次又提到约定大于配置这句话了,SpringBoot中对多配置文件的名字默认识别格式是application-xxx.yml/properties。而默认读取的文件仍然是主配置文件application.yml/properties,而想要切换配置文件,只需在主配置文件中指定想要激活的配置文件即可
yaml多文档块
与properties文件不同,yml使用多配置文件时不仅可以生成多个配置文件,也可以像在java中,一个类文件中可以写多个类,实现在一个配置文件中完成多个配置文件的效果。使用 — 进行分割,就代表多个配置文件了:
server:
port: 8081
spring:
profiles:
active: dev #指定激活的配置文件
---
server:
port: 8082
spring:
profiles: dev
---
server:
port: 8083
spring:
profiles: test
---
使用这种写法,相当于在项目根路径下存在两个名为application-dev和-test的配置文件。而使用spring-profiles-active就可以指定需要激活的配置文件,此处我指定的是dev配置文件生效,项目启动时如果跑着8082端口说明配置生效了:
值得注意的是,如果.properties和.yml文件都存在的情况下,会先读取properties中的配置项。并且多两者会将其中的配置项进行互补。
配置文件读取优先级
官方文档中给出了我们可以放置配置文件的4个位置,他们是存在优先级的:
项目路径下的config文件夹配置文件>项目路径下配置文件>资源路径(classpath)下的config文件夹配置文件>资源路径(classpath)下配置文件
若多个位置上都存在配置文件,它们会按照优先级读取,且和上面yml和properties文件规则一样,如果每个配置文件中的配置项都不一样,也会进行互补。
在配置文件中我们编写一些配置,IDEA会给我们弹出提示:
上一篇文章中简单的说了下自动装配,在这一章通过配置文件将自动装配联系起来再次理解一下。
上一章有说到,自动装配的关键在于启动类中的@EnableAutoConfiguration注解,该注解中导入了AutoConfigurationImportSelector类,该类会扫描带有spring.factories文件的jar,而spring.factories中以键值对的方式记录了类的url,会进行自动装配的类位于EnableAutoConfiguration键值对中,
由于它们都被@Configuration注解修饰,当它们被扫描后会以组件的形式被注入Spring容器,供我们使用。
也就是说,在EnableAutoConfiguration中存在的url才会被自动装配,那么接下来我们在EnableAutoConfiguration任取几个类,看看他们的结构:
这里就随便取了,分别点进去看看:
不难看出,这三个类中都用到了许多相同的注解,这里主要说几个。
首先是@Configuration,这个不用多说,就是将当前类以组件的形式加入到Spring容器中。
其次是@EnableConfigurationProperties,它的功能从名字就不难看出,是将当前类启动配置文件Properties功能,而这是什么功能,在后面的括号中就能看出,应该是将该类与括号中的类相绑定,比如我们点进图三的CacheProperties类中:
到这里应该就很熟悉了,在上面我们将实体类与配置文件的类相绑定时,使用的就是这种方式,这也就解释了为什么我们在配置文件中写配置时会弹出提示。而也能得出结论,配置项也不是我们随便能写的,是需要有相关的配置类才能够配置的。这又是一次约定大于配置的体现
最后是以@Conditional开头的注解,他们其实是属于一类用于断言的注解,当括号中的条件成立时,该类才会生效。也就是当满足一定条件的情况下,当前的类才会被自动装配起来。
总结
以下xxxProperties我将他们称为配置类,xxxAutoConfiguration称为自动装配类