spring-boot-starter-validation校验启动器简述
spring-boot-starter-validation
是 Spring Boot 中的一个启动器依赖,用于在项目中引入数据校验的功能。它基于 javax.validation
(现为 jakarta.validation
)和 Hibernate Validator
实现,为对象属性提供了丰富的校验注解,同时支持自定义校验逻辑。
基本概念
Java API规范即JSR-303
是JavaEE 6
中的一项子规范,又称作 Bean Validation
,提供了针对 Java Bean
字段的一些校验注解,如@NotNull
,@Min
等。JSR-349
是其升级版本,添加了一些新特性。
JSR定义了Bean校验的标准validation-api,但没有提供实现
Hibernate Validator
是对这个规范的实现(与ORM框架无关),并在它的基础上增加了一些新的校验注解如@Email
、@Length
等。
Spring
全面支持了 JSR-303
、JSR-349
规范,对 Hibernate Validation
进行二次封装,在 SpringMVC
模块中添加了自动校验机制,可以利用注解对 Java Bean
的字段的值进行校验,并将校验信息封装进特定的类中。
依赖作用
spring-boot-starter-validation
的主要作用是提供一套基于注解的校验框架,用于验证数据是否合法。该依赖通常用于 Spring Boot 应用中的以下场景:
-
前端数据校验:自动校验传入的请求数据是否符合规定的格式和要求。
-
服务端逻辑校验:确保服务内部的数据符合业务逻辑,以防止数据不一致或异常情况。
-
数据层保护:通过校验确保入库的数据是符合规范的,有助于保持数据的完整性和一致性。
注解使用方式
1. @NotNull
-
作用 :确保字段不为
null
。 -
适用类型 :所有类型(不能为基础数据类型如
int
,因为它们不能为null
)。 -
示例:
@NotNull(message = "用户名不能为空")
private String username;
2. @Null
-
作用 :确保字段为
null
。 -
适用类型:所有类型。
-
示例:
@Null(message = "该字段必须为空")
private String deprecatedField;
3. @NotEmpty
-
作用:确保集合、字符串、数组等不为空(不能为空且大小/长度不能为0)。
-
适用类型:字符串、集合、数组等。
-
示例:
@NotEmpty(message = "列表不能为空")
private List items;
4. @NotBlank
-
作用:确保字符串不为空白(即不能为空,且至少包含一个非空白字符)。
-
适用类型:字符串。
-
示例:
@NotBlank(message = "描述不能为空白字符")
private String description;
5. @Size
-
作用:限制集合、数组或字符串的大小或长度在指定范围内。
-
属性:
-
min
:最小长度(默认为0)。 -
max
:最大长度。
-
-
适用类型:字符串、集合、数组等。
-
示例:
@Size(min = 1, max = 10, message = "用户名长度必须在1到10之间")
private String username;
6. @Min 和 @Max
-
作用:限制数值类型的字段值的最小值和最大值。
-
属性:
value
:允许的最小/最大值。
-
适用类型 :数字类型(如
int
、long
、double
等)。 -
示例:
@Min(value = 18, message = "年龄不能小于18")
@Max(value = 100, message = "年龄不能超过100")
private int age;
7. @Positive 和 @PositiveOrZero
-
作用:
-
@Positive
:确保字段值为正数。 -
@PositiveOrZero
:确保字段值为非负数(即正数或零)。
-
-
适用类型:数字类型。
-
示例:
@Positive(message = "工资必须为正数")
private BigDecimal salary;
8. @Negative 和 @NegativeOrZero
-
作用:
-
@Negative
:确保字段值为负数。 -
@NegativeOrZero
:确保字段值为非正数(即负数或零)。
-
-
适用类型:数字类型。
-
示例:
@NegativeOrZero(message = "债务不能为正数")
private BigDecimal debt;
9. @Past 和 @PastOrPresent
-
作用:
-
@Past
:确保日期在当前日期之前。 -
@PastOrPresent
:确保日期在当前日期或之前。
-
-
适用类型 :
java.util.Date
、java.time.LocalDate
等日期类型。 -
示例:
@Past(message = "生日必须是过去的日期")
private LocalDate birthDate;
10. @Future 和 @FutureOrPresent
-
作用:
-
@Future
:确保日期在当前日期之后。 -
@FutureOrPresent
:确保日期在当前日期或之后。
-
-
适用类型 :
java.util.Date
、java.time.LocalDate
等日期类型。 -
示例:
@Future(message = "预约日期必须是未来的日期")
private LocalDate appointmentDate;
11. @Pattern
-
作用:确保字符串符合指定的正则表达式。
-
属性:
-
regexp
:指定的正则表达式。 -
flags
:正则表达式的匹配标志(如大小写敏感性)。
-
-
适用类型:字符串。
-
示例:
@Pattern(regexp = "^1^{6,12}$", message = "密码必须是6到12位的字母和数字组合")
private String password;
12. @Email
-
作用:确保字符串符合电子邮件格式。
-
属性:
-
regexp
:正则表达式,默认符合标准的邮箱格式。 -
flags
:正则表达式的匹配标志。
-
-
适用类型:字符串。
-
示例:
@Email(message = "请输入有效的邮箱地址")
private String email;
13. @Digits
-
作用:限制数值字段的整数位和小数位的最大位数。
-
属性:
-
integer
:最大整数位数。 -
fraction
:最大小数位数。
-
-
适用类型:数字类型。
-
示例:
@Digits(integer = 5, fraction = 2, message = "金额整数部分不能超过5位,小数部分不能超过2位")
private BigDecimal amount;
14. @DecimalMin 和 @DecimalMax
-
作用:限制字段数值的最小值和最大值(包含边界)。
-
属性:
-
value
:允许的最小或最大值。 -
inclusive
:是否包含边界值,默认为true
。
-
-
适用类型:数字类型。
-
示例:
@DecimalMin(value = "0.0", inclusive = false, message = "金额必须大于0")
private BigDecimal price;
15. @AssertTrue 和 @AssertFalse
-
作用:
-
@AssertTrue
:确保字段值为true
。 -
@AssertFalse
:确保字段值为false
。
-
-
适用类型:布尔类型。
-
示例:
@AssertTrue(message = "必须同意条款")
private Boolean termsAccepted;
示例汇总
以下是将这些注解应用于一个示例实体类的代码:
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
?
public class ExampleEntity {
?
? ?@NotNull(message = "姓名不能为空")
? ?private String name;
?
? ?@Min(value = 18, message = "年龄不能小于18")
? ?@Max(value = 65, message = "年龄不能超过65")
? ?private int age;
?
? ?@Email(message = "邮箱格式不正确")
? ?private String email;
?
? ?@Pattern(regexp = "^[A-Za-z0-9]+$", message = "用户名只能包含字母和数字")
? ?private String username;
?
? ?@Size(min = 1, max = 10, message = "爱好数量必须在1到10之间")
? ?private List<String> hobbies;
?
? ?@Positive(message = "工资必须为正数")
? ?private BigDecimal salary;
?
? ?@Past(message = "入职日期必须是过去的日期")
? ?private LocalDate joinDate;
?
? ?@FutureOrPresent(message = "合同有效期必须在当前日期或未来")
? ?private LocalDate contractExpiryDate;
?
? ?// Getters and Setters
}
常见用法
在 Spring Boot 项目中,spring-boot-starter-validation
常见的用法包括为请求参数、实体类等设置校验注解,并在控制器、服务或数据层进行自动校验。以下是几种常见的注解和用法示例。
1. 基本校验注解
-
@NotNull
:字段不能为空(适用于对象类型)。 -
@NotEmpty
:集合、字符串等字段不能为空,且长度不能为 0。 -
@NotBlank
:字符串字段不能为空,并且必须包含非空白字符。 -
@Size(min = x, max = y)
:字段长度或集合大小在指定范围内。 -
@Min(value)
/@Max(value)
:字段数值不能小于或大于指定值。 -
@Pattern(regexp = "regex")
:字段必须符合正则表达式。
2. 在 DTO 中使用校验注解
在控制器层接收请求参数的 DTO 类上使用这些注解,一遍进行对应校验。例如:
import javax.validation.constraints.*;
?
public class UserDto {
? ?@NotBlank(message = "用户名不能为空")
? ?private String username;
?
? ?@Email(message = "邮箱格式不正确")
? ?private String email;
?
? ?@Size(min = 8, max = 20, message = "密码长度必须在8到20个字符之间")
? ?private String password;
?
? ?@Min(value = 18, message = "年龄不能小于18")
? ?private int age;
?
? ?// Getters and Setters
}
3. 在控制器中应用校验
在控制器中使用 @Validated
或@Valid
注解,使 Spring 自动触发校验规则,如果校验失败会抛出异常。如下:
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
?
@RestController
@RequestMapping("/api/users")
public class UserController {
? ?
? ?@PostMapping
? ?public ResponseEntity<String> createObject(@Validated @RequestBody ObjectDto obj) {
? ? ? ?// 处理业务逻辑
? ? ? ?return ResponseEntity.ok("实体创建成功");
? }
?
? ?@PostMapping
? ?public ResponseEntity<String> createUser(@Valid @RequestBody UserDto user) {
? ? ? ?// 处理业务逻辑
? ? ? ?return ResponseEntity.ok("用户创建成功");
? }
}
参数较少时,也可直接在参数前使用校验注解。如下:
// 路径变量
@GetMapping("{userId}")
public String detail(@PathVariable("userId") @Min(10000000000000000L) Long userId) {
? ?// 校验通过,才会执行业务逻辑处理
? ?UserDTO userDTO = new UserDTO();
? ?userDTO.setUserId(userId);
? ?userDTO.setAccount("11111111111111111");
? ?userDTO.setUserName("xixi");
? ?userDTO.setAccount("11111111111111111");
? ?return "ok";
}
?
// 查询参数
@GetMapping("detail")
public String getDetail(@Length(min = 6, max = 20) @NotNull String ?account) {
? ?// 校验通过,才会执行业务逻辑处理
? ?return "ok";
}
自定义校验
可以通过自定义校验注解和校验器来实现更复杂的校验逻辑。例如,创建一个校验密码复杂度的注解:
注解定义:
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
?
@Documented
@Constraint(validatedBy = PasswordValidator.class)
@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidPassword {
? ?//默认错误消息
? ?String message() default "密码不符合复杂度要求";
? ?//分组
? ?Class<?>[] groups() default {};
? ?//负载
? ?Class<? extends Payload>[] payload() default {};
}
校验器实现:
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
?
public class PasswordValidator implements ConstraintValidator<ValidPassword, String> {
?
? ?@Override
? ?public void initialize(ValidPassword constraintAnnotation) {
? }
?
? ?@Override
? ?public boolean isValid(String password, ConstraintValidatorContext context) {
? ? ? ?return password != null && password.matches("^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$");
? }
}
使用自定义注解:
public class UserDto {
? ?@ValidPassword
? ?private String password;
?
? ?// Getters and Setters
}
全局异常处理
当校验失败时,Spring 会抛出 MethodArgumentNotValidException
或 ConstraintViolationException
异常。可以通过全局异常处理来捕获这些异常并返回自定义的错误响应:
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
?
import javax.validation.ConstraintViolationException;
?
@ControllerAdvice
public class GlobalExceptionHandler {
?
? ?@ExceptionHandler(MethodArgumentNotValidException.class)
? ?@ResponseStatus(HttpStatus.BAD_REQUEST)
? ?@ResponseBody
? ?public Map<String, String> handleValidationExceptions(MethodArgumentNotValidException ex) {
? ? ? ?Map<String, String> errors = new HashMap<>();
? ? ? ?ex.getBindingResult().getFieldErrors().forEach(error ->
? ? ? ? ? ?errors.put(error.getField(), error.getDefaultMessage()));
? ? ? ?return errors;
? }
?
? ?@ExceptionHandler(ConstraintViolationException.class)
? ?@ResponseStatus(HttpStatus.BAD_REQUEST)
? ?@ResponseBody
? ?public String handleConstraintViolationException(ConstraintViolationException ex) {
? ? ? ?return ex.getMessage();
? }
}
分组校验
可以使用分组校验来对同一类对象的不同场景应用不同的校验规则。分组校验的基本思路是通过定义校验分组,并在具体校验时指定校验组来灵活控制哪些规则生效。
1. 定义校验分组接口
首先,为不同的校验场景定义分组接口,例如在创建、更新、删除等不同操作中使用不同的校验规则。
// 创建时的校验组
public interface CreateGroup {
}
// 更新时的校验组
public interface UpdateGroup {
}
2. 在字段上应用分组
在需要校验的字段上,使用 @NotNull
、@Size
等注解的 groups
属性,指定校验组。这使得某些字段在特定的分组下才会触发校验。
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
?
public class UserDto {
?
? ?@NotNull(groups = CreateGroup.class, message = "创建时用户名不能为空")
? ?private String username;
? ?
? ?@NotNull(groups = {CreateGroup.class, UpdateGroup.class, TransferGroup.class})
? ?@Length(min = 6, max = 20, groups = {CreateGroup.class, Update.class})
? ?private String account;
?
? ?@Size(min = 8, groups = UpdateGroup.class, message = "更新时密码长度不能少于8位")
? ?private String password;
?
? ?// getters and setters
? ?
? ?//类内部的分组接口
? ?public interface TransferGroup {
? ? ? ?
? }
}
3. 在控制器方法中指定校验组
在控制器方法中,使用 @Validated
注解指定分组以触发分组校验。需要注意的是,@Valid
注解不会支持分组校验,因此需要使用 @Validated
。
import jakarta.validation.Valid;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
?
@RestController
@RequestMapping("/users")
public class UserController {
?
? ?@PostMapping
? ?public String createUser(@Validated(CreateGroup.class) @RequestBody UserDto userDto) {
? ? ? ?// 只有 CreateGroup 组内的校验生效
? ? ? ?return "User created successfully";
? }
?
? ?@PostMapping
? ?public String updateUser(@Validated(UpdateGroup.class) @RequestBody UserDto userDto) {
? ? ? ?// 只有 UpdateGroup 组内的校验生效
? ? ? ?return "User updated successfully";
? }
? ?
? ?@PostMapping
? ?public String updateUser(@Validated(UserDto.TransferGroup.class) @RequestBody UserDto userDto) {
? ? ? ?// 只有 TransferGroup 组内的校验生效
? ? ? ?return "User transfer successfully";
? }
}
在上面的例子中:
-
创建用户 时,仅
CreateGroup
组内的校验规则会生效(例如username
的@NotNull
校验)。 -
更新用户 时,仅
UpdateGroup
组内的校验规则会生效(例如password
的@Size
校验)。 -
转移用户 时,仅
TransferGroup
组内的校验规则会生效(例如account
的@NotNull
校验)。
4. 结合多个分组
在复杂场景中,可以组合多个分组校验规则。例如在创建和更新操作中,都需要校验某些字段的规则,可以通过传入多个分组来实现组合校验。
@PutMapping("/updateBoth")
public String updateBoth(@Validated({CreateGroup.class, UpdateGroup.class}) @RequestBody UserDto userDto) {
? ?// 同时触发 CreateGroup 和 UpdateGroup 的校验规则
? ?return "User updated with both checks";
}
5. 自定义分组校验逻辑
如果需要自定义复杂的分组校验逻辑,可以在校验注解中指定 groups
,然后在自定义校验器中判断校验组。这样可以对不同的校验组应用不同的逻辑。
6.在分组校验情况下校验未分组的参数
有参数如下
@Data
public class PageDto {
?
? ?/** 当前页码,默认1 */
? ?@NotNull(groups = {PageQueryGroup.class}, message = "页码不能为空")
? ?@Min(value = 1, message = "页码不能小于1")
? ?private Long currentPage = 1L;
?
? ?/** 每页条数,默认10 */
? ?@NotNull(groups = {PageQueryGroup.class}, message = "每页条数不能为空")
? ?@Min(value = 1, message = "每页条数不能小于1")
? ?private Long pageSize = 10L;
}
当控制器进行分组校验时,@Min
未生效
@GetMapping("/page")
public Result page(@Validated(PageQueryGroup.class) PageDto pageDto){
? ?//后续代码校验通过后执行
}
出现这种情况的原因在于 @Validated(PageQueryGroup.class)
指定了校验组 PageQueryGroup.class
,因此只会校验与该组匹配的注解(即 @NotNull(groups = {PageQueryGroup.class})
),而 @Min
因未指定分组,因此未被包含在 PageQueryGroup.class
组的校验中。
为了让 @Min
也生效,需要使用自带默认组javax.validation.groups.Default
javax.validation.groups.Default
这个默认分组本质就是在无任何分组的校验注解下(如@NotNull
、@Min
)也是无任何分组下@Validated
默认校验的分组,源码如下:
package javax.validation.groups;
?
public interface Default {
? ?
}
要让@Min在无分组下及PageQueryGroup
下都生效,可选方法如下
方法1:增加@Min的生效分组
@NotNull(groups = {PageQueryGroup.class}, message = "页码不能为空")
@Min(value = 1, groups = {Default.class, PageQueryGroup.class}, message = "页码不能小于1")
private Long currentPage = 1L;
这样,该注解既能在不指定分组的校验下生效,本次分组下也会生效
方法2:控制器中增加指定默认分组
@GetMapping("/page")
public Result page(@Validated(PageQueryGroup.class, Default.class) PageDto pageDto){
? ?//后续代码校验通过后执行
}
这样,控制器下自动校验既会校验PageQueryGroup
分组,也会校验默认分组
两种方法使用其中一种,或两种同时使用,都能达成效果
嵌套校验
嵌套校验用于处理对象内部的属性也是一个对象的情况,通常应用于复杂对象结构的校验。要实现嵌套校验,需要在嵌套对象的属性上使用 @Valid
注解,确保内部对象的字段也会被递归校验。以下是嵌套校验的具体步骤和注意事项:
1. 定义嵌套结构的对象
例如,我们有一个 Order
对象,包含一个 Customer
对象,并且需要校验 Order
和 Customer
的字段。
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
?
public class Customer {
? ?@NotNull(message = "Customer name cannot be null")
? ?private String name;
?
? ?@Size(min = 10, max = 15, message = "Phone number must be between 10 and 15 characters")
? ?private String phoneNumber;
?
? ?// Getters and Setters
}
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
?
public class Order {
? ?@NotNull(message = "Order ID cannot be null")
? ?private String orderId;
?
? ?@Valid // 确保嵌套校验生效
? ?@NotNull(message = "Customer details are required")
? ?private Customer customer;
?
? ?@NotEmpty(message = "Order items cannot be empty")
? ?private List<String> items;
?
? ?// Getters and Setters
}
在 Order
类中,我们将 Customer
对象标注为 @Valid
,表示在校验 Order
时,也会对其 customer
属性中的字段进行递归校验。
2. 使用嵌套校验
在控制器方法中使用 @Valid
或 @Validated
对 Order
对象进行校验:
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*;
?
@RestController
@RequestMapping("/orders")
public class OrderController {
?
? ?@PostMapping
? ?public String createOrder(@Valid @RequestBody Order order) {
? ? ? ?// 如果校验失败,将会抛出异常
? ? ? ?return "Order created successfully";
? }
}
在此例中,@Valid
将对 Order
对象执行校验,同时对嵌套的 Customer
对象中的字段进行递归校验。如果 Customer
中的 name
或 phoneNumber
不符合约束条件,将会返回相应的错误信息。
3. 处理校验失败的响应
Spring 默认会返回一个 400 Bad Request
错误响应,并包含详细的校验错误信息。若需要自定义校验错误响应,可以使用 @ControllerAdvice
和 @ExceptionHandler
进行处理:
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.MethodArgumentNotValidException;
?
import java.util.HashMap;
import java.util.Map;
?
@ControllerAdvice
public class ValidationExceptionHandler {
?
? ?@ResponseStatus(HttpStatus.BAD_REQUEST)
? ?@ExceptionHandler(MethodArgumentNotValidException.class)
? ?public Map<String, String> handleValidationExceptions(MethodArgumentNotValidException ex) {
? ? ? ?Map<String, String> errors = new HashMap<>();
? ? ? ?ex.getBindingResult().getFieldErrors().forEach(error -> errors.put(error.getField(), error.getDefaultMessage()));
? ? ? ?return errors;
? }
}
此异常处理程序会捕获校验异常 MethodArgumentNotValidException
,并将错误信息以字段名和错误信息的形式返回给客户端。
4. 嵌套分组校验
如果有不同的校验场景(例如创建和更新),还可以结合分组校验。在嵌套对象中指定校验组时,同样需要在外层对象的字段上标记 @Validated(Group.class)
来激活分组校验。
5. 嵌套校验的限制
-
嵌套校验通常只适用于一层嵌套,如果存在更深层嵌套结构,
@Valid
仍会递归校验,但需要确保各层对象的校验约束清晰、合理,避免深度过大的对象结构。 -
若嵌套校验对象中存在集合(如
List
、Set
等),@Valid
会自动对集合中的每个元素进行递归校验。
集合校验
如果请求体直接传递了json数组给后台,并希望对数组中的每一项都进行参数校验。此时,如果我们直接使用java.util.Collection
下的list
或者set
来接收数据,参数校验并不会生效!我们可以使用自定义list集合来接收参数:
包装List类型,并声明@Valid注解
public class ValidationList<E> implements List<E> {
?
? ?@Delegate // @Delegate是lombok注解
? ?@Valid // 一定要加@Valid注解
? ?public List<E> list = new ArrayList<>();
? ?
? ?// 一定要记得重写toString方法
? ?@Override
? ?public String toString() {
? ? ? ?return list.toString();
? }
?
}
@Delegate
注解受lombok版本限制,1.18.6以上版本可支持。如果校验不通过,会抛出NotReadablePropertyException
,同样可以使用统一异常进行处理。
比如,我们需要一次性保存多个User对象,Controller层的方法可以这么写:
@PostMapping("/saveList")
public Result saveList(@RequestBody @Validated(UserDTO.Save.class) ValidationList<UserDTO> userList) {
? ?// 校验通过,才会执行业务逻辑处理
? ?return Result.ok();
}
手动校验(编程式校验)
上述都是基于注解来实现自动校验的,在某些情况下,如在业务实现的过程中,我们可能需要手动调用校验逻辑。这个时候可以注入javax.validation.Validator
对象,然后再调用其api。
@Autowired
private javax.validation.Validator globalValidator;
?
// 手动校验
@PostMapping("/saveWithCodingValidate")
public Result saveWithCodingValidate(@RequestBody UserDTO userDTO) {
//这里的validate方法第一个参数是需要校验的对象,后续是校验分组,分组可以为0个(此时为默认分组),也可以为多个
? ?Set<ConstraintViolation<UserDTO>> validate = globalValidator.validate(userDTO, Save.class);
? ?// 如果校验通过,validate为空;否则,validate包含未校验通过项
? ?if (!validate.isEmpty()) {
// 校验失败
// 抛出异常方法一
for (ConstraintViolation<UserDTO> violation: validate) {
? ? ? ? ? ?System.out.println(violation.getMessage());
throw new RuntimException(violation.getMessage());
? ? ? }
// 抛出异常方法二
throw new RuntimException(validate.iterator().next().getMessage());
? }
// 校验通过才会执行到这里
? ?return Result.ok();
}
快速失败(Fail Fast)
Spring Validation
默认会校验完所有字段,然后才抛出异常。可以通过一些简单的配置,开启Fali Fast
模式,一旦校验失败就立即返回
1.在 application.properties
中配置:
spring.validation.fail-fast=true
yaml文件参照配置即可。无需编写额外的代码即可启用快速失败,简单快捷,适合一般场景。
2.自定义 Validator
配置------通过 LocalValidatorFactoryBean
实现 Validator
的 Bean 配置:
import org.hibernate.validator.HibernateValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
?
@Configuration
public class ValidationConfig {
?
? ?@Bean
? ?public LocalValidatorFactoryBean validator() {
? ? ? ?LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
? ? ? ?validator.getValidationPropertyMap().put("hibernate.validator.fail_fast", "true");
? ? ? ?return validator;
? }
}
上述方法使用 Spring 提供的 LocalValidatorFactoryBean
类,并通过 getValidationPropertyMap()
方法设置属性 hibernate.validator.fail_fast
为 true
,以启用快速失败模式。这种方式更加贴近 Spring 的配置方式,通常用于需要与 Spring 的其他功能(如国际化等)集成的场景。这是因为能够更好地与 Spring 的依赖注入和国际化功能配合。例如,它会自动使用 messages.properties
等国际化资源文件,并能与 Spring 中的 @Autowired
配合。
3.自定义 Validator
配置------直接调用 Validation.byProvider(HibernateValidator.class).configure().failFast(true)
来创建 Validator
@Bean
public Validator validator() {
? ?ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
? ? ? ? ? .configure()
? ? ? ? ? ?// 快速失败模式
? ? ? ? ? .failFast(true)
? ? ? ? ? .buildValidatorFactory();
? ?return validatorFactory.getValidator();
这是标准 JSR-303 的配置方式,适合不依赖于 Spring 上下文的场景,也可以直接用于非 Spring 框架的项目中。这种方式更灵活,可通过标准配置自定义 ConstraintValidatorFactory
等属性,但在 Spring 上下文中可能不支持某些 Spring 特有的功能,如国际化。
使用场景和适用性
方式
配置方式
特点
使用场景
spring.validation.fail-fast=true
配置文件
简单快捷,适合一般需求
轻量级应用、常规项目中的快速配置
LocalValidatorFactoryBean
自定义 Validator
Bean
深度整合 Spring 上下文,支持国际化
需要国际化、多模块项目或复杂配置场景
ValidatorFactory
标准 ValidatorFactory
灵活,适合非 Spring 场景,不支持 Spring 国际化
标准 Validator 或非 Spring 场景
优缺点总结
方式
优点
缺点
spring.validation.fail-fast=true
简单易用,零代码改动
仅能启用快速失败,配置不灵活
LocalValidatorFactoryBean
深度整合 Spring,支持国际化和资源绑定
需要编写自定义配置类
ValidatorFactory
配置灵活,适用标准 JSR 303 项目
配置较繁琐,不支持 Spring 特性
实现原理
见参考文献中第二篇 wh柒八九 所著 Spring Validation校验_springvalidation-CSDN博客
总结
spring-boot-starter-validation
提供了一个便捷的校验框架,通过注解的方式即可对请求数据进行强制约束。常见的用法包括在 DTO 中设置注解、在控制器中启用校验,以及通过全局异常处理提供更友好的反馈。
参考文献
validation检查框架_validation框架-CSDN博客
Spring Validation校验_springvalidation-CSDN博客
- a-zA-Z0-9 ↩︎