java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > JAVA校验@Valid和@Validated

JAVA校验之@Valid和@Validated实践指南

作者:Easocen

在Java开发中参数校验是保障程序健壮性的重要手段,@Valid和@Validated是两个常用的校验注解,但它们在使用场景、验证方式和嵌套处理等方面存在显著差异,这篇文章主要介绍了JAVA校验之@Valid和@Validated的相关资料,需要的朋友可以参考下

概述

区别

校验类型

实践

分组校验

分组校验允许你根据不同的业务场景(如创建、更新、删除等)对同一个实体类应用不同的验证规则,从而实现更灵活和精细的控制。

特别注意

创建分组

// 用于创建操作的校验组
public interface CreationGroup { }

// 用于更新操作的校验组
public interface UpdateGroup { }

// 你还可以定义其他分组,例如:
// public interface DeleteGroup { }

设置实体

在实体类的字段上添加校验注解,并使用 groups 属性指定该校验规则属于哪个分组。一个字段可以属于多个分组。

@Data
public class UserBean {

    // 创建和更新时都校验用户名非空
    // 默认的 Default 组也适用,但如果指定了分组,则只在指定分组下生效
    @NotEmpty(message = "用户名不能为空", groups = {CreationGroup.class, UpdateGroup.class})
    private String username;

    // 年龄校验,无分组,默认在所有场景(包括未指定分组的 @Validated)下都校验
    @Min(value = 18, message = "年龄不能小于18岁")
    private Integer age;

    // 邮箱格式校验,无分组,默认在所有场景下都校验
    @Email(message = "邮箱格式不正确")
    private String email;

    // ID 校验,只在更新时生效,且必须大于等于1
    @NotNull(message = "ID不能为空", groups = UpdateGroup.class) // 更新时ID不能为null
    @Min(value = 1, message = "ID必须大于0", groups = {UpdateGroup.class})
    private Long id;

    // ... 其他属性
}

使用校验

在 Controller 方法的参数上使用 @Validated 注解,并传入需要激活的校验组。

import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid; // 导入 javax.validation.Valid

@RestController
@RequestMapping("validation")
public class ValidationController {

    /**
     * 更新用户接口:只激活 UpdateGroup 组的校验规则。
     * 此时,UserBean 中的 id 字段会根据 UpdateGroup 的规则进行校验。
     * username 和 age、email 字段则会根据它们自身定义的(或默认的)规则进行校验。
     */
    @GetMapping("updateUser")
    public UserBean updateUser(@Validated({UpdateGroup.class}) UserBean userBean){
        // 如果校验失败,会抛出 MethodArgumentNotValidException,由全局异常处理器捕获
        System.out.println("更新用户成功: " + userBean);
        return userBean;
    }

    /**
     * 创建用户接口:只激活 CreationGroup 组的校验规则。
     * 此时,UserBean 中的 username 字段会根据 CreationGroup 的规则进行校验。
     * age 和 email 字段则会根据它们自身定义的(或默认的)规则进行校验。
     * id 字段由于不属于 CreationGroup,将不会被校验。
     */
    @GetMapping("createUser")
    public UserBean createUser(@Validated({CreationGroup.class}) UserBean userBean){
        // 如果校验失败,会抛出 MethodArgumentNotValidException,由全局异常处理器捕获
        System.out.println("创建用户成功: " + userBean);
        return userBean;
    }

    // 注意:如果一个方法参数不加 @Validated,但实体类中有 @Valid 注解,
    // 则会默认校验所有无分组的字段(即 Default 组)。
    // 例如:
    // @PostMapping("defaultValid")
    // public UserBean defaultValid(@Valid @RequestBody UserBean userBean) {
    //     return userBean;
    // }
}

嵌套验证

嵌套校验(Nested Validation) 指的是在验证外部对象时,对其内部包含的其他对象进行递归验证的过程。当一个对象中包含另一个对象作为属性,并且需要对这个被包含的对象也进行验证时,就需要进行嵌套校验。

嵌套属性指的是在一个对象中包含另一个对象作为其属性的情况。换句话说,当一个对象的属性本身又是一个对象(非基本类型),那么这些被包含的对象就可以称为嵌套属性

特别提示

验证嵌套属性

首先,定义你的嵌套实体类,并为其字段添加校验规则:

import lombok.Data;
import javax.validation.constraints.NotBlank;

@Data
public class AddressBean {
    @NotBlank(message = "国家不能为空")
    private String country;
    @NotBlank(message = "城市不能为空")
    private String city;
}

接着,在主实体类中包含这个嵌套属性,并在该属性上添加 @Valid 和 @NotNull(如果地址对象本身不能为 null):

@Data
public class UserBean {

    @NotEmpty(message = "用户名不能为空", groups = {CreationGroup.class})
    private String username;

    @Min(value = 18, message = "年龄不能小于18岁")
    private Integer age;

    @Email(message = "邮箱格式不正确")
    private String email;

    // 嵌套验证必须要加上 @Valid
    // 同时可以添加 @NotNull 来确保 address 对象本身不为空
    @Valid
    @NotNull(message = "地址信息不能为空")
    private AddressBean address; // 嵌套属性

    // ... 其他属性和 getter/setter
}

然后在 Controller 中使用 @Validated 来触发校验:

@RestController
@RequestMapping("validation")
public class ValidationController {

    /**
     * 嵌套验证示例。
     * 当 @RequestBody UserBean userBean 传入时,
     * 如果 userBean 内部的 address 属性有 @Valid 注解,
     * 那么 address 对象自身的校验规则也会被触发。
     * 此处如果 address 为 null 或其内部字段(country, city)为空,都会触发校验失败。
     */
    @PostMapping("nestValid")
    public UserBean nestValid(@Validated @RequestBody UserBean userBean){ // @Validated 也可以不指定分组,则校验 Default 组
        System.out.println("嵌套验证成功: " + userBean);
        return userBean;
    }

    /**
     * 带分组的嵌套验证示例。
     * 此时,只有 UserBean 中属于 CreationGroup 的校验规则生效,
     * 并且这个分组也会传递给嵌套的 AddressBean,如果 AddressBean 中也有分组校验规则,它们就会生效。
     * 此外,因为 address 属性是 @Valid 且 @NotNull,它会始终被校验。
     */
    @PostMapping("nestValidWithGroup")
    public UserBean nestValidWithGroup(@Validated(CreationGroup.class) @RequestBody UserBean userBean){
        System.out.println("带分组的嵌套验证成功: " + userBean);
        return userBean;
    }
}

验证集合

当请求体是一个对象的集合(如 List<YourBean>)时,校验的原理与嵌套验证类似,你需要将 @Valid 放在集合参数前,而 @Validated 则用于指定校验组(如果需要)。同时,你也可以对集合本身进行非空校验。

@RestController
@RequestMapping("/metadataTemplate")
public class ReMetadataTemplateController {

    // 假设 reMetadataTemplateService 已注入

    /**
     * 更改元数据模板状态接口,支持对列表本身非空及列表中每个对象的字段进行分组校验。
     *
     * @param list 待更新的元数据模板列表
     * @return 结果
     */
    @PutMapping("/changeStatus")
    @Validated(EditGroup.class) // 指定激活 EditGroup 组的校验
    public Void changeStatus(
        // @Valid 触发对 List 中每个 ReMetadataTemplateBo 对象的校验
        // @NotEmpty(message = "参数不能为空") 校验 List 本身不能为 null 或空集合
        @Valid @NotEmpty(message = "元数据模板列表不能为空", groups = EditGroup.class)
        @RequestBody List<ReMetadataTemplateBo> list
    ) {
        // 如果校验失败,会抛出 MethodArgumentNotValidException,由全局异常处理器捕获
        // ... 业务逻辑
        reMetadataTemplateService.changeStatus(list);
    }
}

异常处理

为了更好地处理校验失败时抛出的异常,通常需要配置一个全局异常处理器

引用

到此这篇关于JAVA校验之@Valid和@Validated实践指南的文章就介绍到这了,更多相关JAVA校验@Valid和@Validated内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文