SpringBoot-(十八)SpringBoot读取配置文件和参数校验

本文最后更新于:June 7, 2022 pm

SpringBoot框架中有两个非常重要的策略:开箱即用和约定优于配置。其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。

目录

创建一个SpringBoot项目,并进行相应的配置。

配置文件

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ttf: www.tothefor.com

tothefor:
name: 墨水记忆
website: tothefor.com
description: 最好的记忆不如最淡的墨水
tags:
- name: Java
description: Java类
- name: C
description: C类
- name: PHP
description: PHP类

配置读取

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.tothefor.controller;

import io.lettuce.core.output.SocketAddressOutput;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @Author DragonOne
* @Date 2022/6/7 13:15
* @墨水记忆 www.tothefor.com
*/

@Controller
@RestController
public class TestController {

@Value("${ttf}")
private String name;
@Value("${tothefor.name}")
private String web;

@GetMapping("/getInfo")
public void show(){
System.out.println(name);
System.out.println(web);
}


}

访问 http://localhost:8080/getInfo 后,查看控制台输出:

1
2
www.tothefor.com
墨水记忆

Bean读取

通过@ConfigurationProperties读取配置信息并与 bean 绑定。

配置信息对应的类:(注意一定要写对应的setter和getter)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.tothefor.controller;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.util.List;

/**
* @Author DragonOne
* @Date 2022/6/7 14:43
* @墨水记忆 www.tothefor.com
*/
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Component
@ConfigurationProperties(prefix = "tothefor")
class TestDTO {

private String name;
private String website;
private String description;

private List<Book> tags;

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
static class Book{
String name;
String description;
}

}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.tothefor.controller;

import io.lettuce.core.output.SocketAddressOutput;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @Author DragonOne
* @Date 2022/6/7 13:15
* @墨水记忆 www.tothefor.com
*/

@Controller
@RestController
public class TestController {

@Autowired
private TestDTO testDTO;

@GetMapping("/getInfo")
public void show(){
System.out.println(testDTO.getName());
System.out.println(testDTO.getWebsite());
System.out.println(testDTO.getDescription());
System.out.println(testDTO.getTags());
}


}

输出:

1
2
3
4
墨水记忆
tothefor.com
最好的记忆不如最淡的墨水
[TestDTO.Book(name=Java, description=Java类), TestDTO.Book(name=C, description=C类), TestDTO.Book(name=PHP, description=PHP类)]

以下示例均通过Postman进行测试。

参数校验

在前端对数据进行校验的情况下,我们还是要对传入后端的数据再进行一遍校验,避免用户绕过浏览器直接通过一些 HTTP 工具直接向后端请求一些违法数据。

JSR(Java Specification Requests) 是一套 JavaBean 参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们 JavaBean 的属性上面,这样就可以在需要校验的时候进行校验了,非常方便!而 SpringBoot 项目的 spring-boot-starter-web 依赖中已经有 hibernate-validator 包,不需要引用相关依赖。

:更新版本的 spring-boot-starter-web 依赖中不再有 hibernate-validator 包(如2.3.11.RELEASE),需要自己引入 spring-boot-starter-validation 依赖。

常用的字段验证的注解

  • @NotEmpty 被注释的字符串的不能为 null 也不能为空
  • @NotBlank 被注释的字符串非 null,并且必须包含一个非空白字符
  • @Null 被注释的元素必须为 null
  • @NotNull 被注释的元素必须不为 null
  • @AssertTrue 被注释的元素必须为 true
  • @AssertFalse 被注释的元素必须为 false
  • @Pattern(regex=,flag=)被注释的元素必须符合指定的正则表达式
  • @Email 被注释的元素必须是 Email 格式。
  • @Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
  • @Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
  • @DecimalMin(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
  • @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
  • @Size(max=, min=)被注释的元素的大小必须在指定的范围内
  • @Digits(integer, fraction)被注释的元素必须是一个数字,其值必须在可接受的范围内
  • @Past被注释的元素必须是一个过去的日期
  • @Future 被注释的元素必须是一个将来的日期

测试

导入依赖:(注意使用时,是使用的javax包下的,而不是org包下的)

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

请求体校验

实体类(RequestBody):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.tothefor.controller;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import javax.validation.constraints.*;

/**
* @Author DragonOne
* @Date 2022/6/7 15:10
* @墨水记忆 www.tothefor.com
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class CheckTestDto {

@NotNull(message = "名称不能为空")
@Size(max = 15,min = 5,message = "请输入在5-15范围内的长度")
private String name;

@Min(value = 13,message = "最小不得低于13")
private Integer minAge;
@Max(value = 23,message = "最大不得高于23")
private Integer maxAge;

@Email(message = "email 格式不正确")
@NotNull(message = "email 不能为空")
private String email;

}

在需要验证的参数上加上了@Valid注解,如果验证失败,它将抛出MethodArgumentNotValidException

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.tothefor.controller;

import io.lettuce.core.output.SocketAddressOutput;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

/**
* @Author DragonOne
* @Date 2022/6/7 13:15
* @墨水记忆 www.tothefor.com
*/

@Controller
@RestController
public class TestController {

@PostMapping("/getInfo")
public void show(@RequestBody @Valid CheckTestDto checkTestDto){
System.out.println("参数全部符合要求");
}

}

测试样例

全部符合

1
2
3
4
5
6
{
"name":"tothefor",
"minAge":13,
"maxAge":23,
"email":"123456798@qq.com"
}

正确输出:

1
2
参数全部符合要求
tothefor

minAge不符合

1
2
3
4
5
6
{
"name":"tothefor",
"minAge":10, <!-- 低于了13 -->
"maxAge":23,
"email":"123456798@qq.com"
}

报错:

1
2022-06-07 15:38:25.731  WARN 1463 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public void com.tothefor.controller.TestController.show(com.tothefor.controller.CheckTestDto): [Field error in object 'checkTestDto' on field 'minAge': rejected value [10]; codes [Min.checkTestDto.minAge,Min.minAge,Min.java.lang.Integer,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [checkTestDto.minAge,minAge]; arguments []; default message [minAge],13]; default message [最小不得低于13]] ]

name不符合

1
2
3
4
5
6
{
"name":"ttf",
"minAge":13,
"maxAge":23,
"email":"123456798@qq.com"
}

报错:

1
2022-06-07 15:39:34.212  WARN 1463 --- [nio-8080-exec-7] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public void com.tothefor.controller.TestController.show(com.tothefor.controller.CheckTestDto): [Field error in object 'checkTestDto' on field 'name': rejected value [ttf]; codes [Size.checkTestDto.name,Size.name,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [checkTestDto.name,name]; arguments []; default message [name],15,5]; default message [请输入在5-15范围内的长度]] ]

其他的情况同理。

请求参数校验

分为Path Variables 和 Request Parameters。一定要在类上加上 @Validated 注解,这个参数可以告诉 Spring 去校验方法参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.tothefor.controller;

import io.lettuce.core.output.SocketAddressOutput;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.Size;

/**
* @Author DragonOne
* @Date 2022/6/7 13:15
* @墨水记忆 www.tothefor.com
*/

@Controller
@RestController
@Validated //不写也可以正常运行
public class TestController {

@PostMapping("/getInfo/{check}")
public void show(@Valid @PathVariable("check") @Size(max = 15,min = 5,message = "请输入在5-15范围内的长度") String check){
System.out.println("参数符合要求");
System.out.println(check);
}

}

测试样例

符合要求

1
2
参数符合要求
tothefor

不符合要求

1
javax.validation.ConstraintViolationException: show.check: 请输入在5-15范围内的长度

上面讲了不符合要求时会抛出异常,现在就简单说一说怎么处理这个异常。

异常处理(Controller)

相关注解:

  1. @ControllerAdvice :注解定义全局异常处理类
  2. @ExceptionHandler :注解声明异常处理方法

我拿上面最后一个示例说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.tothefor.controller;

import io.lettuce.core.output.SocketAddressOutput;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.Size;

/**
* @Author DragonOne
* @Date 2022/6/7 13:15
* @墨水记忆 www.tothefor.com
*/

@Controller
@RestController
@Validated //不写也可以正常运行
public class TestController {

@PostMapping("/getInfo/{check}")
public void show(@Valid @PathVariable("check") @Size(max = 15,min = 5,message = "请输入在5-15范围内的长度") String check){
System.out.println("参数符合要求");
System.out.println(check);
}

}

异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.tothefor.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
* @Author DragonOne
* @Date 2022/6/7 15:57
* @墨水记忆 www.tothefor.com
*/
@ControllerAdvice(assignableTypes = {TestController.class}) //这里需要修改为自己对应的Controller
@ResponseBody
public class ExcetipnConfig {

@ExceptionHandler(value = Exception.class)// 拦截所有异常, 这里只是为了演示,一般情况下一个方法特定处理具体某一种异常,如:MethodArgumentNotValidException.class
public ResponseEntity<?> exceptionHandler(Exception e) {

if (e instanceof IllegalArgumentException) {
System.out.println("IllegalArgumentException");
return ResponseEntity.status(400).body("IllegalArgumentException");
} else if(e instanceof MethodArgumentNotValidException){
System.out.println("MethodArgumentNotValidException");
return ResponseEntity.status(404).body("MethodArgumentNotValidException");
}
System.out.println("其他异常");
return null;
}
}