2025-03-18 10:09阅读: 38评论: 0推荐: 0

【SpringBoot】注解校验

问题起源

Controller层有个接口参数对象AA有个属性是List<B> ,List<B> 不允许为空
B有个属性是List<C> ,list<C> 允许为空。但是如果有值,那么
C的属性id和name,date不能为空。

在SpringBoot中,如何用注解进行非空校验?

解决方案简述:
Controller层对A对象添加@Valid
然后为List<B> 添加@NotNull, @Valid
然后为List<C> 添加, @Valid
然后为C的字符串类型属性添加@NotEmpty,为Data类型的属性添加@NotNull

在Controller的方法参数上使用@Valid注解,触发校验。
在A类中的List字段上使用@Valid,以级联校验B对象。
在B类中的List字段上使用@Valid,以级联校验C对象。
在C类中的具体字段添加合适的注解

接下来就分析:
1 @Valid 有什么用
2 有哪些校验注解,对应着什么类型

1. @Valid 有什么用

主要用于 触发 Bean 的校验机制

在 Spring Boot 中,@Valid 是 Java Bean Validation(JSR 303、JSR 349、JSR 380)规范提供的注解,Spring 框架对其进行了支持。

基本作用

(1)触发校验:当在 Controller 方法的参数上使用 @Valid 注解时,Spring 会自动调用 Bean Validation 框架对该参数对象进行校验。如果对象的属性违反了任何校验规则,Spring 会抛出 MethodArgumentNotValidException 异常。

(2)级联校验:@Valid 可以实现级联校验,即当一个对象包含另一个对象作为属性时,使用 @Valid 注解可以递归地对嵌套对象的属性进行校验。例如,在前文提到的应用场景中,在 A 类的 List<B> 属性上使用 @Valid,可以触发对 B 对象的校验;在 B 类的 List<C> 属性上使用 @Valid,可以触发对 C 对象的校验。

2. 有哪些校验注解

2.1. @Valid 有什么用

在Spring Boot中,@Valid 是Java Bean Validation(JSR 303、JSR 349、JSR 380)规范提供的注解,Spring框架对其进行了支持。它主要用于触发Bean的校验机制,其具体作用如下:

基本作用

  • 触发校验:当在Controller方法的参数上使用 @Valid 注解时,Spring会自动调用Bean Validation框架对该参数对象进行校验。如果对象的属性违反了任何校验规则,Spring会抛出 MethodArgumentNotValidException 异常。
  • 级联校验@Valid 可以实现级联校验,即当一个对象包含另一个对象作为属性时,使用 @Valid 注解可以递归地对嵌套对象的属性进行校验。例如,在你的场景中,在 A 类的 List<B> 属性上使用 @Valid,可以触发对 B 对象的校验;在 B 类的 List<C> 属性上使用 @Valid,可以触发对 C 对象的校验。

示例代码

import javax.validation.Valid;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    @PostMapping("/example")
    public String handleRequest(@Valid @RequestBody A a) {
        // 处理请求
        return "Success";
    }
}

2.2. 有哪些校验注解,对应着什么类型

Java Bean Validation规范提供了一系列的校验注解,以下是一些常用的校验注解及其适用类型:

基本注解

注解 适用类型 作用
@NotNull 任何引用类型(如对象、数组、集合等) 校验对象不能为 null,但可以是空字符串、空集合等。
@NotEmpty CharSequence(如 String)、CollectionMap、数组 校验对象不能为 null,并且长度或大小必须大于 0。
@NotBlank CharSequence(如 String 校验字符串不能为 null,并且去除首尾空格后长度必须大于 0。
@Null 任何引用类型 校验对象必须为 null

数值相关注解

注解 适用类型 作用
@Min(value = x) Number 及其子类(如 IntegerLong 等) 校验数值必须大于或等于指定的值 x
@Max(value = x) Number 及其子类(如 IntegerLong 等) 校验数值必须小于或等于指定的值 x
@DecimalMin(value = "x") BigDecimalBigIntegerCharSequencebyteshortintlong 及其包装类 校验数值必须大于或等于指定的值 x,可以处理小数。
@DecimalMax(value = "x") BigDecimalBigIntegerCharSequencebyteshortintlong 及其包装类 校验数值必须小于或等于指定的值 x,可以处理小数。
@Digits(integer = x, fraction = y) BigDecimalBigIntegerCharSequencebyteshortintlong 及其包装类 校验数值的整数部分位数不能超过 x,小数部分位数不能超过 y

日期相关注解

注解 适用类型 作用
@Past java.util.Datejava.util.Calendarjava.time.Instantjava.time.LocalDatejava.time.LocalDateTime 校验日期必须是过去的日期。
@Future java.util.Datejava.util.Calendarjava.time.Instantjava.time.LocalDatejava.time.LocalDateTime 校验日期必须是未来的日期。

其他注解

注解 适用类型 作用
@Pattern(regexp = "regex") CharSequence(如 String 校验字符串必须匹配指定的正则表达式。
@Size(min = x, max = y) CharSequence(如 String)、CollectionMap、数组 校验对象的长度或大小必须在指定的范围 [x, y] 内。

3 注解校验的优劣分析

1 优点

(1)代码简洁性
减少重复代码:使用注解进行校验可以避免在代码中编写大量的条件判断语句来进行参数验证。例如,若不使用注解,为了验证一个用户对象的姓名不为空,可能需要编写类似 if (user.getName() == null || user.getName().isEmpty()) 的代码。而使用 @NotEmpty 注解,只需要在 name 属性上添加该注解即可,大大减少了重复的验证逻辑代码。
可读性强:注解直接标注在属性或方法参数上,能够清晰地表明该属性或参数需要满足的校验规则。开发人员可以一目了然地知道每个字段的验证要求,提高了代码的可读性和可维护性。

(2)与框架集成度高
Spring 集成便利:在 Spring Boot 项目中,注解校验与 Spring 框架深度集成。只需要在 Controller 方法的参数上添加 @Valid 注解,Spring 就会自动调用 Bean Validation 框架进行校验,并且会自动处理校验失败的情况,抛出 MethodArgumentNotValidException 异常,开发人员可以通过全局异常处理器统一处理该异常,实现统一的错误响应格式。
与其他组件协同工作:可以与 Spring 的其他组件(如 AOP)结合使用,进一步增强校验功能。例如,可以使用 AOP 在方法执行前后进行额外的校验或处理。

(3)可维护性好
易于修改规则:当业务需求发生变化,需要修改校验规则时,只需要修改相应的注解参数即可,而不需要修改大量的验证逻辑代码。例如,原本要求某个字段的长度范围是 [1, 10],使用 @Size(min = 1, max = 10) 注解,若需求变为 [2, 15],只需修改注解参数为 @Size(min = 2, max = 15) 即可。
便于扩展:Java Bean Validation 规范支持自定义校验注解,开发人员可以根据业务需求创建自定义的校验注解,以满足特定的验证要求。例如,创建一个自定义注解来验证身份证号码的格式是否正确。

(4)跨项目复用性
校验规则复用:注解校验规则可以在不同的项目中复用。只要项目使用了 Java Bean Validation 框架,就可以将定义好的校验注解和校验逻辑在多个项目中共享,提高了开发效率。

2 缺点

(1)功能有限
① 复杂逻辑难以实现:对于一些复杂的业务逻辑验证,注解可能无法满足需求。例如,需要根据多个字段的值进行联合验证,或者需要进行数据库查询来验证某个字段的唯一性等情况,单纯使用注解很难实现。此时,可能需要编写额外的代码来完成这些复杂的验证逻辑。
② 缺乏动态性:注解的校验规则是在编译时确定的,缺乏动态性。如果需要根据不同的业务场景动态调整校验规则,注解可能无法很好地支持。

(2)调试困难
错误信息不明确:当校验失败时,注解抛出的异常信息可能不够详细,尤其是在嵌套对象的校验中,很难快速定位到具体哪个字段的校验失败。开发人员可能需要花费更多的时间来调试和排查问题。
异常处理复杂:虽然 Spring 可以统一处理校验失败的异常,但在处理复杂的业务场景时,可能需要对不同类型的校验失败进行不同的处理,这会增加异常处理的复杂度。

(3)性能开销
反射调用开销:注解校验是基于反射机制实现的,反射调用会带来一定的性能开销。在高并发场景下,频繁的反射调用可能会影响系统的性能。不过,在大多数情况下,这种性能开销是可以接受的。
校验逻辑开销:对于嵌套对象的校验,需要递归地对每个嵌套对象进行校验,这也会增加一定的性能开销。尤其是当嵌套层次较深或对象数量较多时,性能影响可能会更加明显。

posted @   萌狼蓝天  阅读(38)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开