SpringBoot 配置
配置文件、加载顺序、配置原理
1. 配置文件
SpringBoot 默认使用两种类型的配置文件作为一个全局配置文件,配置文件名固定
,用于修改SpringBoot自动配置的默认值
- application.properties
- application.y(a)ml
1.1 YAML简介
YAML(YAML Ain't Markup Language)递归缩写
- YAML A Markup Language:是一个标记语言;
- YAML isn't Markup Language:不是一个标记语言;
YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言)。
YAML:以数据为中心,比json、xml等更适合做配置文件
XML
<server>
<port>8081</port>
</server>
YAML
server:
port: 8081
YAML与XML、json相比,没有像XML中大量冗余的格式标签,以数据为中心
1.2 YAML语法
1.2.1 基本语法
- 键值对的写法:
- 以空格的缩进【缩进不允许使用tab,只允许空格】来控制层级关系,空格的多少没有要求,只要物理位置上处于同一列,那就是同一层级
- 属性和值大小写敏感
- '#'表示注释
1.2.2 值的写法
1.2.2.1 值为字面量
范围:数字、字符串、日期、布尔
写法:直接写
注:字符串不用加上单引号或双引号,单引号或双引号的字符串有特殊含义
- 双引号包围的字符串:"",不会转义字符串里面的特殊字符
举例:"a \n b" 实际含义 "a 换行 b" - 单引号包围的字符串:'',会转义字符串里面的特殊字符
举例:"a \n b" 实际含义 "a \n b"
1.2.2.2 值为对象、Map
写法一:在下一行写对象的属性和值的关系
写法二:行内写法
1.2.2.3 值为数组
范围:List、Set、Array
写法一:用- 值
表示数组中的一个元素
写法二:行内写法
1.3 JavaBean中注入配置文件的值
1.3.1 y(a)ml配置文件
JavaBean
/**
* 将配置文件中配置的每一个属性的值,映射到这个组件中
* @ConfigurationProperties(prefix = "person"):告诉SpringBoot将本类中的所有属性和配置文件中前缀为person的配置进行绑定,默认从全局配置文件中获取值。
* @Component:只有这个组件是容器中的组件,才能使用容器提供的@ConfigurationProperties功能
*/
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String lastName;
private Integer age;
private Boolean boos;
private Date birthday;
private Map<String, Object> maps;
private List<Object> lists;
private Dog dog;
注:IDEA中Alt+Insert快捷生成get、set、toString方法
为了编写配置文件时有提示功能,可以导入SpringBoot配置文件处理器。
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!--由于spring-boot-configuration-processor仅仅是用于提高开发体验的,在项目打包的时候不需要将这个工具打进去-->
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
注:spring-boot-configuration-processor引入后需要重启SpringBoot应用使该工具生效
配置文件:application.yml
person:
last-name: libing
age: 24
birthday: 1997/08/21
boos: true
maps:
k1: xxx
k2: 111
lists: [1,2,3]
dog:
name: xg
age: 2
注:配置文件中的last-name ⇔ Person中的lastName属性,两种写法都可以
1.3.2 properties配置文件
JavaBean不变,注释掉application.yml中的内容。
application.properties,注意map和list的写法,与y(a)ml文件有很大差异
person.last-name=离能
person.age=24
person.birthday=1997/08/21
person.boos=true
person.maps.k1=xxx
person.maps.k2=111
person.lists=1,2,3
person.dog.name=xg
person.dog.age=2
遗留问题:IDEA properties文件中文乱码尚未解决,视频中的方法也不行
1.3.3 @Value和@ConfigurationProperties的区别
Tip:
- 松散绑定(Relaxed Binding)是一种属性名匹配规则,如下:
- @Value注解value属性的值:字面量/${key}从环境变量、配置文件中获取值/#{SpEL}
结论:
在某个业务逻辑中获取配置文件中的某项值,用@Value;
JavaBean和配置文件进行映射,用@ConfigurationProperties
1.3.4 @ConfigurationProperties和数据校验
导入坐标
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
这样在将配置文件中的值注入JavaBean具体的属性之前,会进行校验,如果校验不通过,则无法进行注入
1.3.5 @PropertySource
加载指定的properties配置文件并将值注入到JavaBean的属性中
@ConfigurationProperties只能加载全局配置文件,如果是自定义的properties文件,是无法加载的,而@PropertySource可以。
首先自定义一个properties配置文件
JavaBean内容如下
1.3.6 @ImportResource
导入Spring的配置文件,让配置文件中的内容生效。用法是将@ImportResource标注在一个配置类(可以是主配置了也可以是其他配置类)上
自定义的Spring配置文件如下
主配置类如下
测试方法如下
package com.atguigu.springboot;
import com.atguigu.springboot.bean.Person;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
@SpringBootTest
class SpringBoot01HelloworldQuickApplicationTests {
@Autowired
Person per;
// 自动注入Spring容器
@Autowired
ApplicationContext ioc;
@Test
void contextLoads() {
System.out.println(per);
}
@Test
void testImportResource(){
// ioc.containsBean("helloService"):判断Spring容器中是否有helloService这个组件
System.out.println(ioc.containsBean("helloService"));
}
}
上述例子中beans.xml以配置文件的方式向Spring添加组件,SpringBoot并不推荐这样的方式。
Spring推荐给容器中添加组件的方式(全注解的方式):
- 新增一个配置类
这里的配置类是跟配置文件等价的 - 使用@Bean给容器中添加组件
1.4 配置文件占位符
- 随机数
- 获取之前配置的值,如果之前没有设置可以用
:
指定默认值
1.5 Profile多环境支持
SpringBoot用于对不同环境提供不同的配置。实现的方式有两种,多Profile文件
方式和多Profile文档块
方式。
1.5.1 多Profile文件方式
- 适用于properties和y(a)ml文件
- 设置环境配置文件的格式:
application-{环境标识}.properties/y(a)ml
- 环境标识支持自定义,一般,dev(开发环境)、prod(生产环境)、test(测试环境)
- 不额外指定激活,默认生效的环境是application.properties/application.y(a)ml
1.5.2 多Profile文档块方式
对于y(a)ml文件来说,可以通过指定主配置文件内部多Profile文档块设置多个环境,Profile文档块直接通过---
进行分割,当指定的Profile文档块环境被激活后,该Profile文档块中的配置都被激活。
- 仅适用于y(a)ml文件
- 环境标识支持自定义,一般,dev(开发环境)、prod(生产环境)、test(测试环境)
- 不额外指定激活,默认生效的环境是application.yml中最顶部的Profile文档块
1.5.3 激活方式
即使主配置文件中没有进行多环境的配置(多Profile文档块、多Profile文件),也可以通过命令行和JVM参数激活不同的环境
- 配置文件激活:主配置文件内指定激活
- 命令行参数激活
或者打包后在运行jar包时通过java -jar XXX.jar --spring.profiles.active=环境标识指定 - JVM参数激活
注:三种激活方式的优先级:配置文件<JVM参数<命令行参数
1.6 主配置文件的加载位置
主配置文件的加载位置是指主配置文件可以放在那些地方,这些地方SpringBoot都会进行加载。
- 当前项目下的config目录下
- 当前项目根目录下
- 类路径下的config目录下
- 类路径根目录下
上述四个位置的优先级是由高到低的,对于重叠的配置项,高优先级配置会覆盖低优先级配置,配置项不发生重叠的,互补配置
互补配置的好处:低优先级配置文件配置所有配置项,高优先级配置文件配置部分内容
另外,可以通过命令行参数的方式(- -spring.config.location)指定临时加载的配置文件位置,这种方式的配置文件优先级最高。
1. 7 外部配置加载顺序
SpringBoot也可以从一下位置加载配置,优先级从高到低,高优先级的配置会覆盖低优先级的配置,所有的配置会形成互补配置。
命令行参数
- 来自java:comp/env的JNDI属性
- java系统属性(System.getProperties())
- 操作系统环境变量
- RandomValuePropertySource配置的random.*属性值
jar包外部的application-{profile}.properties或application-{profile}.y(a)ml(带spring.profile的)配置文件
application-{profile}.properties或application-{profile}.y(a)ml与jar包放在同一目录下就可生效jar包内部的application-{profile}.properties或application-{profile}.y(a)ml(带spring.profile的)配置文件
jar包外部的application.properties或application.y(a)ml(不带spring.profile的)配置文件
jar包内部的application.properties或application.y(a)ml(不带spring.profile的)配置文件
- @Configuration注解类上的@PropertySource
- 通过SpringApplication.setDefaultProperties指定的默认属性
6-9说明了优先加载带环境标识的配置文件,如果都是带环境标识的配置文件,加载顺序是先加载jar包外的,再加载jar包内的
这里的优先加载意思是哪个最终生效哪个就是优先加载,而不是优先加载的要优先读取
上述的加载来源并不是全部的,参考官方文档
1.8 自动配置原理
自动配置原理:
- SpringBoot启动的时候加载主配置类,开启了自动配置功能(@EnableAutoConfiguration)
- @EnableAutoConfiguration的作用
2.1 利用EnableAutoConfigurationImportSelector给容器中导入一些组件
2.2 具体导入了哪些组件从AutoConfigurationImportSelector类的selectImports()方法中可以查看
2.3 selectImports()方法中有List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);
这么一条语句,用于获取候选的配置
2.4 getCandidateConfigurations()方法内部利用SpringFactoriesLoader.loadFactoryNames()扫描所有jar包类路径下的META-INF/spring.factories
文件
2.5 把扫描到的每一个文件的内容封装成Properties对象
2.6 从封装后的这些Properties对象中获取到EnableAutoConfiguration.class类(类名)对应的值,这里的每个值都是一个XXXAutoConfiguration类,把他们添加到容器中,用他们来做自动配置
- 每一个自动配置类进行自动配置功能,以HttpEncodingAutoConfiguration为例进一步解释自动配置原理
@Configuration //表示这是一个配置类,跟以前的配置文件一样,也可以给容器中添加组件。
@EnableConfigurationProperties(HttpEncodingProperties.class) //启动HttpEncodingProperties类的ConfigurationProperties功能,将配置文件中对应的值和HttpEncodingProperties绑定起来(配置文件的值注入到HttpEncodingProperties属性中),并把HttpEncodingProperties加入到IOC容器中
@ConditionalOnWebApplication //涉及Spring底层的@Conditional注解,可以根据不同的条件,判定当前配置类是否生效。在这里的作用是,判断当前应用是否为Web应用,如果是,当前配置类才生效。
@ConditionalOnClass(CharacterEncodingFilter.class) //判断当前项目有没有CharacterEncodingFilter(SpringMVC中解决乱码的过滤器)这个类
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true) //判断当前配置文件中是否存在spring.http.encoding=enabled这个配置,matchIfMissing = true表明,即使我们在配置文件中未配置该项,配置类也会生效。
public class HttpEncodingAutoConfiguration {
//获取到的HttpEncodingProperties已经和配置文件进行了绑定。
private final HttpEncodingProperties properties;
//Spring中的构造函数注入,会从IOC容器中取出HttpEncodingProperties的对象,赋值给当前类的properties属性。
public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
this.properties = properties;
}
//自动配置类给容器中添加组件时,这些组件的某些值是要从properties中获取的
@Bean
@ConditionalOnMissingBean(CharacterEncodingFilter.class)
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
return filter;
}
一旦这个配置类生效,这个配置类就会给容器中添加各种组件,这些组件的属性是从对应的properties类中获取的,而这些properties类中的每一个属性又是和配置文件绑定的。
上面提到的XXXProperties类的属性和配置文件中的可配置项是一一对应的,配置文件中能配置的配置项就可以参考对应的XXXProperties类
整个自动配置过程的逻辑流程图如下
1.8.1 @Conditional&自动配置报告
@Conditional是Spring的注解,SpringBoot中有一些常用的派生注解,是基于@Conditional注解的,作用是当满足条件时后面的代码就会生效
SpringBoot中的自动配置类通常都是满足一定条件下才生效的,那我们怎么知道哪些自动配置类生效了呢,可以开启SpringBoot的Debug模式。
主配置文件中
这样,项目启动时控制台就会打印自动配置报告,告诉我们哪些自动配置类启用了,哪些没有启用