SpringBoot核心配置
SpringBoot配置文件分类
SpringBoot是基于约定的,所以很多配置都有默认值,但如果想使用自己的配置替换默认配置的话,就可以使用
application.properties或者application.yml(application.yaml)进行配置
-
application.properties
语法:key=value
server.port=8080
-
application.yml
语法:key:空格 value (冒号后必须要用空格)
server: port: 8080
小结:
- SpringBoot提供了2种配置文件类型:properteis和yml/yaml
- 默认配置文件名称:application
- 在同一级目录入如果三类文件共存,优先级为:properties > yml > yaml
YAML概述
YAML全称是 YAML Ain’t Markup Language 。YAML是一种直观的能够被电脑识别的的数据数据序列化格式,并且容易被人类阅读,容易和脚本语言交互的,可以被支持YAML库的不同的编程语言程序导入,比如:C/C++, Ruby, Python, Java, Perl, C#, PHP等。YML文件是以数据为核心的,比传统的xml方式更加简洁。YAML文件的扩展名可以使用.yml或者.yaml。
properties:
server.port=8080
server.address=127.0.0.1
xml:
<server>
<port>8080</port>
<address>127.0.0.1</address>
</server>
yaml:
server:
port: 8080
address: 127.0.0
YAML基础语法
- 大小写
- 数据值前边必须有空格,作为分隔符
- 使用缩进表示层级关系
- 缩进时不允许使用Tab键,只允许使用空格(各个系统 Tab对应的 空格数目可能不同,导致层次混乱)
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
- "#"表示注释,从这个字符一直到行尾,都会被解析器忽略。
YAML数据格式
-
基本数据类型:
#基本数据类型 boolean: True #TRUE,true,True,FALSE,false,False均可 float: 3.14 #6.8523015e+5 #支持科学计数法 int: 123 #0b1010_0111_0100_1010_1110 支持二进制、八进制、十六进制 string: HelloWorld #字符串可以直接书写
-
数组:一组按次序排列的值( List、set )
# 数组 address: - beijing - shanghai #行内写法 commpany: [阿里,华为,腾讯]
-
对象、Map(键值对)
#对象 person: name: 张三 age: 25 #行内写法 person1: {name:张三,age:25} #对象数组格式一 users: - name: 张三 age: 4 - name: 李四 age: 5 #对象数组格式二 users2: - name: 张三 age: 4 - name: 李四 age: 5 #对象数组缩略格式 users3: [{name:张三,age:4},{name:李四,age:5}]
-
YAML参数引用:
#参数引用 name: 王老七 person3: name: ${name} age: 22 sex: 男
-
小结:
- 配置文件类型
- properties:和以前一样 (设置文件的语言编码UTF-8)
- yml/yaml:注意空格
- yaml:简洁,以数据为核心
- 基本语法
- 大小写敏感
- 数据值前边必须有空格,作为分隔符
- 使用空格缩进表示层级关系,相同缩进表示同一级
- 数据格式
- 对象
- 数组:使用”-“表示数组的每个元素
- 基本数据类型
- 参数引用
- ${key}(EL表达式)
- 基本语法
- 配置文件类型
读取配置文件内容
yaml文件更强大的地方在于,他可以给我们的实体类直接注入匹配值
支持方式:
逐个注入:@Value
批量注入:
@ConfigurationProperties
Enviroment类
逐个注入
演示:
properties:
@Component //注册bean
public class Dog {
@Value("阿黄")
private String name;
@Value("18")
private Integer age;
}
加载指定的配置文件**@PropertySource** **:**加载指定的配置文件
yml:resources目录下新建一个.yml文件
name: 阿黄
age: 18
@PropertySource(value = "classpath:dog.yml")
@Component //注册bean
public class Dog{
@Value("${name}")
private String name;
@Value("${age}")
private Integer age;
}
批量注入
方式一:
Person类
@Component //注册bean到容器中
public class Person {
private String name;
private integer age;
private boolean marry;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
//有参无参构造、get、set方法、toString()方法
}
Dog类:
public class Dog{
private String name;
private int age;
//有参无参构造、get、set方法、toString()方法
}
编写yaml文件:
person1:
name: wangls
age: 18
marry: true
birth: 1990/10/19
maps: {k1: v1,k2: v2}
lists:
- code
- bodybuilding
- music
dog:
name: summer
age: 1
数据注入到类中:
@Component //注册bean
@ConfigurationProperties(prefix = "person1")
public class Person {
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
}
@ConfigurationProperties的作用:
将配置文件中配置的每一个属性的值,映射到这个组件中;告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定参数 prefix = “person” : 将配置文件中的person下面的所有属性一一对应
如果编写完配置文件有红色提示,则可以添加坐标隐藏
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
测试:
@SpringBootTest
class SpringbootInitApplicationTests {
@Autowired
Person person;
@Test
void contextLoads() {
System.out.println(person);
}
}
结果:
Person{name=‘刘大脑袋’, age=22, birth=Mon Jan 01 00:00:00 CST 1990, lists=[drink, eat, play, happy], maps={car=BMW, pc=ROG}, dog=Dog{name=‘summer’, age=1}}
方式二:
直接在测试类中装配开发模式类辅助获取key对应value数据
@Autowired
private Environment env;
@Test
public void test(){
System.out.println(env.getProperty("person1.name"));
System.out.println(env.getProperty("person1.age"));
System.out.println(env.getProperty("person1.dog.name"));
System.out.println(env.getProperty("person1.lists[0]"));
}
@Value这个使用起来并不友好!我们需要为每个属性单独注解赋值,比较麻烦;我们来看个功能对比图
- @ConfigurationProperties只需要写一次即可 , @Value则需要每个字段都添加
- 松散绑定:这个什么意思呢? 比如我的yml中写的last-name,这个和lastName是一样的, - 后面跟着的字母默认是大写的。这就是松散绑定。
- JSR303数据校验 , 这个就是我们可以在字段是增加一层过滤器验证 , 可以保证数据的合法性
- 复杂类型封装,yml中可以封装对象 , 使用value就不支持
配置yml和配置properties都可以获取到值 , 强烈推荐 yml;
如果我们在某个业务中,只需要获取配置文件中的某个值,可以使用一下 @value;
如果说,我们专门编写了一个JavaBean来和配置文件进行一一映射,就直接@configurationProperties,不要犹豫!
JSR303数据校验
对于 web 服务来说,为防止非法参数对业务造成影响,在 Controller 层一定要做参数校验的!大部分情况下,请求参数分为如下两种形式
- POST 、 PUT 请求,使用 requestBody 传递参数;
- GET 请求,使用 requestParam/PathVariable 传递参数。
什么是JSR303标准:
JSR的全称是Java Specification Requests(Java 规范提案),是指向JCP ( Java CommunityProcess )提出新增一个标准化技术规范的正式请求。
Java API 规范( JSR303 )定义了 Bean 校验的标准 validation-api ,但没有提供实现。 hibernatevalidation 是对这个规范的实现,并增加了校验注解如 @Email 、 @Length 等。 SpringValidation 是对 hibernate validation 的二次封装,用于支持 spring mvc 参数自动校验。接下来,我们以 spring-boot 项目为例,介绍 Spring Validation 的使用
JSR303校验注解的使用步骤
-
注入依赖,导入spring-boot-starter-validation启动器,pom文件导入坐标
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
-
在实体类需要校验的成员字段(Field)上,添加校验注解。
@Component @ConfigurationProperties(prefix = "person4") @Validated public class Person { @NotNull(message = "姓名不能为空") private String name; @Max(message = "年龄不能超过上限",value= 120) private int age; private Date birth; private List<String> lists; private Map<String,Object> maps; private Dog dog; // 有参无参构造方法,get,set,toString()方法 }
yml:
person4: name: 刘大脑袋 age: 220 birth: 1990/1/1 lists: [drink,eat,play,happy] maps: {car: BMW,pc: ROG} dog: name: summer age: 1
测试:
@SpringBootTest class SpringbootInitApplicationTests { @Autowired Person person; @Test void contextLoads() { System.out.println(person); } }
报错信息:
请求的参数校验
实体类:Dog
@Component
public class Dog {
@NotNull(message = "狗狗的名字不能为空")
private String name;
@Max(message = "狗狗年龄超过上限了",value = 20)
private int age;
// 有参无参构造方法,get,set,toString()方法
}
在Controller控制器的校验参数前,使用@Valid注解开启校验,使用BindingResult 绑定校验结果
@RequestMapping(path = "/vali")
@ResponseBody
public String validationDemo(@Valid Dog dog, BindingResult result) {
int errorCount = result.getErrorCount();
if (errorCount > 0) {
List<FieldError> fieldErrors = result.getFieldErrors();
for (FieldError fieldError : fieldErrors) {
System.out.println("属性:{" + fieldError.getField() + "},传参是{" + fieldError.getRejectedValue() + "},报错提示信息{" + fieldError.getDefaultMessage() + "}");
}
return "数据不合法";
} else {
return "数据合法";
}
}
主程序启动项目,访问URL:localhost:8080/vali?name=旺财&age=100
页面显示:数据不合法
控制台:打印拼接的报错信息
Controller统一异常处理
@ControllerAdvice:统一为Controller进行"增强"
@ExceptionHandler : 异常处理
实现步骤:
-
控制器
@RequestMapping(path = "/vali") @ResponseBody public String validationDemo(@Valid Dog dog) { return "数据合法"; }
-
新建包和类:
@ControllerAdvice // 统一异常处理注释 public class BindExceptionHandler { @ExceptionHandler(BindException.class) // 捕获的异常类型 @ResponseBody public String handlerBindException(BindException ex, HttpServletRequest request){ return "全局异常处理器捕获成功"; } }
-
Dog实体类同上
-
测试:
主程序启动项目,访问URL:localhost:8080/vali?name=旺财&age=100
页面显示:全局异常处理器捕获成功
JSR303校验注解分类
- 值校验
// 被注解的元素必须为null
@Null(message = "必须为null")
// 被注解的元素必须不为null
@NotNull(message = "必须不为null")
// 校验注解的元素值不为空(不为null、去除首位空格后长度为0),类型为String
@NotBlank(message = "必须不为空")
// 校验注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0),类型为CharSequence、Collection、Map、Array
@NotEmpty(message = "必须不为null且不为空")
// 被注解的元素必须为true,并且类型为boolean
@AssertTrue(message = "必须为true")
// 被注解的元素必须为false,并且类型为boolean
@AssertFalse(message = "必须为false")
- 范围校验
// 被注解的元素其值必须大于等于最小值,并且类型为int,long,float,double
@Min(value = 18, message = "必须大于等于18")
// 被注解的元素其值必须小于等于最小值,并且类型为int,long,float,double
@Max(value = 18, message = "必须小于等于18")
// 校验注解的元素值大于等于@DecimalMin指定的value值,并且类型为BigDecimal
@DecimalMin(value = "150", message = "必须大于等于150")
// 校验注解的元素值小于等于@DecimalMax指定的value值 ,并且类型为BigDecimal
@DecimalMax(value = "300", message = "必须大于等于300")
// 校验注解的元素值在最小值和最大值之间,并且类型为BigDecimal,BigInteger,CharSequence,byte,short,int,long。
@Range(max = 80, min = 18, message = "必须大于等于18或小于等于80")
// 被注解的元素必须为过去的一个时间,并且类型为java.util.Date
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Past(message = "必须为过去的时间")
// 被注解的元素必须为未来的一个时间,并且类型为java.util.Date
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Future(message = "必须为未来的时间")
- 长度校验
// 被注解的元素的长度必须在指定范围内,并且类型为String,Array,List,Map
@Size(max = 11, min = 7, message = "长度必须大于等于7或小于等于11")
// 校验注解的元素值长度在min和max区间内 ,并且类型为String
@Length(max = 11, min = 7, message = "长度必须大于等于7或小于等于11")
- 格式校验
// 校验注解的元素值的整数位数和小数位数上限 ,并且类型为float,double,BigDecimal
@Digits(integer=3,fraction = 2,message = "整数位上限为3位,小数位上限为2位")
// 被注解的元素必须符合指定的正则表达式,并且类型为String
@Pattern(regexp = "\\d{11}",message = "必须为数字,并且长度为11")
// 校验注解的元素值是Email,也可以通过regexp和flag指定自定义的email格式,类型为String
@Email(message = "必须是邮箱")
多环境切换profile
我们在开发Spring Boot应用时,通常同一套程序会被安装到不同环境,比如:开发、测试、生产等。其中数据库地址、服务 器端口等等配置都不同,如果每次打包时,都要修改配置文件,那么非常麻烦。profile功能就是来进行动态配置切换的;
命名语法:
例如:application-环境简称.properties/yml
- application-dev.properties/yml 开发环境
- application-test.properties/yml 测试环境
- application-pro.properties/yml 生产环境
-
profile配置方式:
- 多profile文件方式
-
profile激活方式:
-
配置文件
spring: profiles: active: test #环境简称
-
虚拟机参数 :在VM options 指定:-Dspring.profiles.active=dev
-
命令行参数:java –jar xxx.jar --spring.profiles.active=dev
-
配置文件加载位置
-
配置文件加载顺序
Springboot程序启动时,会从以下位置加载配置文件: 1. file:./config/:当前项目下的/config目录下 2. file:./ :当前项目的根目录 3. classpath:/config/:classpath的/config目录 4. classpath:/ :classpath的根目录 加载顺序为上文的排列顺序,高优先级配置的属性会生效
-
外部配置加载顺序
通过官网查看外部属性加载顺序:
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config
项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;这种情况,一般是后期运维做的多,相同配置,外部指定的配置文件优先级最高
java -jar XXX.jar --spring.config.location=F:/application.properties