SpringBoot
1. 前言
在上一篇博客中,我们分析了SpringBoot
自动配置的原理。通过自动配置,其会依照我们搭建项目时给予的组件之间依赖性为我们自动配置好了组件能正常启动相关的配置,我们直接启动即可,因为设置的是默认值,当一些业务情况需要更改配置默认值时,SpringBoot
也提供了解决方案,那就是提供配置文件让我们修改默认值,SpringBoot
会将配置文件中配置的属性值设置为组件相应的配置值。
2. SpringBoot 配置文件的种类
SpringBoot
使用一个全局的配置文件,配置文件名是固定的:
- application.properties
- application.yml
properties
文件格式的配置文件,在使用快速向导创建SpringBoot
应用的时候,默认就在resources
目录下创建了,因此它是默认的全局配置文件,这种配置文件语法是“key=value
”的形式;
properties
:配置示例
server.port=8081
YAML
(YAML Ain’t Markup Language
)语言的文件,文件语法使用空白,缩进,分行组织数据,yml
文件采取树状结构,更加简洁易读,以数据为中心,比json
、xml
等更适合做配置文件。
YAML
:配置示例
server:
port: 8081
XML
:配置示例
<server>
<port>8081</port>
</server>
3. 两种配置文件的区别
3.1 内容格式比较
properties
文件语法是“key=value
”的形式,对于复杂属性数据,是以“.
”号连接层级关系的。结构上没有分层效果;
而yml
文件采用树状结构,结构上有明显的分层效果,语法是以"key:(空格)value
"的形式,以空间的缩进来控制层级关系,只要是左对齐的一列数据,都是属性同一层级的。
3.2 执行顺序
工程中同时存在application.properties
文件和 application.yml
文件,yml
文件会先加载,而后加载的properties
文件会覆盖yml
文件。所以建议工程中,只使用其中一种类型的文件即可。
可以看到,properties
和yml
文件采用不同的端口,最终是以properties
文件的内容为准。
4. YML 语法
4.1 基本语法
key:(空格)value
: 表示一对键值对(空格必须有)
以空格的缩进来控制层级关系、只要是左对齐的一列数据,都是同一个层级的。
server:
port: 8081
path: /hello
属性和值也是大小写敏感的。
4.2 值的写法
4.2.1 字面量: 普通的值(数字、字符串、布尔)
key: value: 字面量
字符串默认不用加上单引号或者双引号
""
:双引号 ;不会转义字符里面的特殊字符;特殊字符会作为本身想表示的意思。
name: "zhangsan \n lisi"
, 输出: zhangsan 换行 lisi
''
:单引号 ;会转义字符里面的特殊字符;特殊字符最终只是一个普通的字符串数据
name: 'zhangsan \n lisi'
输出:zhangsan \n lisi
4.2.2 对象、Map(属性和值)(键值对)
key: value: 在下一行来写对象的属性和值的关系
;注意缩进
对象还是key: value: 值
的方式。
friends:
lastName: zhangsan
age: 20
行内写法:
friends: {lastName: zhangsan,age: 18}
4.2.3 数组(List、Set)
用- 值
表示数组中的一个元素
pets:
- cat
- dog
- pig
行内写法:
pets: [cat,dog,pig]
5. 两种配置文件获取值的方式
配置文件内容
person:
lastName: zhangsan
age: 18
boss: false
birth: 2018/1/1
maps: {k1: v1 ,k2: v2}
lists:
- lisi
- wangwu
dog:
name: xiaogou
age: 3
5.1 通过 @ConfigurationProperties 注解
@ConfigurationProperties
注解作用是告诉SpringBoot
将本类中的所有属性与配置文件中的属性进行绑定,它需要提供prefix
属性,可通过统一前缀批量将类中属性和配置文件中属性进行绑定,使用该方式的时候需要引入配置文件处理器依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
并且使用添加@Component
、@Configuration
或者@EnableConfigurationProperties
注解,将类作为容器中的组件,才能使用容器中的功能。
示例代码如下:
package com.example.demo.entity;
import org.springframework.stereotype.Component;
@Component
@Data
public class Dog {
private String name;
private int age;
}
5.1.1 使用@Component注解
package com.example.demo.entity;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
@Component
@ConfigurationProperties(prefix = "person")
@Data
public class Person {
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
}
5.1.2 使用 @configuration 注解
package com.example.demo.entity;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
@configuration
@ConfigurationProperties(prefix = "person")
@Data
public class Person {
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
}
5.1.3 使用 @EnableConfigurationProperties 注解
package com.example.demo.entity;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
@ConfigurationProperties(prefix = "person")
@Data
public class Person {
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
}
@SpringBootApplication
@EnableConfigurationProperties(Person.class)
public class DemoApplication {
@Autowired
private Person person;
@RequestMapping(value = "/hello", method = RequestMethod.GET)
@ResponseBody
public String hello() {
System.out.println("person" + person);
return "hello world";
}
public static void main(String[] args) {
//SpringApplication.run(TestProperty1.class, args);
new SpringApplicationBuilder(DemoApplication.class).web(SERVLET).run(args);
}
}
5.2 通过 @Value 注解
@Value
注解类似在原始Spring
项目在配置文件中配置bean
标签,在bean
标签中配置了properties
标签,并提供value
属性值,例如:
<bean class="xxx">
<properties name="xxx" value=""/>
</bean>
@Value注解相当于这个properties标签中的value属性值。
使用@Value
注解不能通过前缀批量将类中所有属性与配置文件中特定属性绑定,它支持使用${key}
(从环境变量、配置文件中获取值)、#{SpEl}
等。
比如:
@Value("${person.last-name}")
private String name;
@Value("#{11*2}")
private int age;
@Value("true")
private boolean flag;
将配置文件中的属性绑定到类中的属性时,对于对象属性,如果键包含除小写字母数字字符或-
以外的任何字符,则需要使用括号表示法,以便保留原始值。如果键未被[]
包围,则将删除任何非字母数字或-
的字符。比如map属性:
#yml文件写法
#自定义map集合
person:
maps:
"[/key1]": value1
/key3: value3
k2: v2
#properties文件写法
#自定义map集合
person.maps.[/key1]=va2
person.maps./key3=v2
那么使用注解获取到的map集合内容为maps={/key1=va2, key3=v2}
5.3 @ConfigurationProperties 和 @Value 注解不同
5.3.1 松散绑定
比如类中定义了一个属性,属性名为firstName
,如果使用@ConfigurationProperties
,那么配置文件中,该属性可以写成如下形式:
person.firstName:使用标准方式
person.first-name:大写用-,建议在.properties和.yml文件中使用。
person.first_name:大写用_,它是用于.properties和.yml文件的可选格式。
PERSON.FIRST_NAME 大写格式,建议在使用系统环境变量时使用。
但是如果使用@Value
注解,那么只能写成@Value("${person.firstName}")
。
每个属性源的宽松绑定规则
5.3.2 JSR303 数据校验
@ConfigurationProperties
注解支持JSR303
数据校验,意味着进行数据校验的属性必须符合规则才给予通过,否则报错。比如给类中的某个属性添加@Email
注解,则规定,该属性值必须符合邮箱格式才进行通过。查看代码:
@Validated
public class Person {
@Value("${person.last-name}")
@Email
private String name;
}
5.3.3 关于绑定类中的属性是否需要提供set、get方法
这个情况只适应于使用@ConfigurationProperties
注解。由于绑定是通过标准javaBean
属性,因此绝大情况下属性都需要提供setter
和getter
方法,除以下情况可以省略setter
方法:
- 集合或者数组可以通过索引(通常使用yaml)或使用单个逗号分隔值(属性)访问。对于数组属性,
setter
方法是强制的。我们建议始终为此类类型添加setter
方法。如果初始化集合,请确保它不是不可变的。 - 初始化了嵌套的POJO属性,例如:person类中有一个属性:private Dog dog=new Dog();则不需要setter方法,但是是private Dog dog;则需要提供setter方法。如果希望绑定器使用其默认构造函数动态创建实例,则需要一个setter方法。
- 最后,只考虑标准的JavaBean属性,不支持对静态属性的绑定。
6. 配置文件占位符
SpringBoot
配置文件中允许使用随机数或者在配置文件中引用前面配置过的属性来作为占位符。
比如使用随机数的占位符:
${random.int}
${random.long}
${random.value}
${random.uuid}
${random.int(10)}
${random.int[1024,65523]}
比如在文件中引用前面配置过的属性作为占位符:
app.name=MyApp
app.decription=${app.name} is a SpringBoot Application
如果配置文件上下文没有该属性时,我们随便输入一个属性名,那么它将会把这个属性名打印出来,比如:
app.name=Myapp
app.decription=${MyProject} is a SpringBoot Application
那么使用注解获取app.decription
这个属性值时,打印的结果为:MyProject is a SpringBoot Application
我们还可以为这个配置文件上下文都没有的属性值赋值。比如如下操作:
app.name=Myapp
app.decription=${MyProject:customProject} is a SpringBoot Application
那么使用注解获取app.decription
这个属性值时,打印的结果为:customProject is a SpringBoot Application
。
7. profile
7.1多 Profile 文件
我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml
;
默认使用application.properties
7.2 yml 支持多文档块方式
server:
port: 8081
spring:
profiles:
active: dev
---
server:
port: 8082
spring:
profiles: dev
---
server:
port: 8083
spring:
profiles: prod
7.3 激活指定 Profile
-
在配置文件中指定
spring.profiles.active=dev
-
命令行:
--spring.profiles.active=dev
- 虚拟机参数:
-Dspring.profiles.active=dev
8. 配置文件加载位置
SpringBoot
启动会扫描以下位置的application.properties
或者application.yml
文件作为SpringBoot
的默认配置文件
- file:./config/
- file:./
- classpath:/config/
- classpath:/
以上是按照优先级从高到低的顺序,所有位置的文件都会被加载,高优先级配置内容会覆盖低优先级配置内容。
我们也可以通过配置spring.config.location来改变默认配置。
Spring Boot会从这四个位置全部加载主配置文件;互补配置;
我们也可以通过spring.config.location来改变默认配置
项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定配置文件和默认加载的这些配置文件一起生效。
9. 外部配置加载顺序
SpringBoot
也可以从以下位置加载配置,优先级从高到低。高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置。
- 命令行参数
java -jar spring-boot-04-config-0.0.1-SNAPSHOT.jar --server.port=8087 --server.context-path=/abc
多个配置项空格分隔,写法:--配置项=值
-
来自
java:comp/env
的JNDI
属性 -
Java
系统属性(System.getProperties()
) -
操作系统环境变量
-
RandomValuePorpertySource
配置的random.*属性值
由jar包外向jar包内进行寻找
优先加载带profile
-
jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件
-
jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件
再来加载不带profile
-
jar包外部的application-{profile}.properties或application.yml(不带spring.profile)配置文件
-
jar包内部的application-{profile}.properties或application.yml(不带spring.profile)配置文件
-
@Configuration
注解类上的@PropertySource
-
通过
SpringApplication.setDefaultProperties
指定默认属性