ParamValidator使用

举报
developer_Li 发表于 2025/04/03 14:43:13 2025/04/03
【摘要】 @Valid与 ParamValidator.validate1. @Valid 的特点使用方式:@Valid 是 Java Bean Validation 的注解(通常结合 Hibernate Validator 使用),用于自动验证方法参数或字段的约束。配合 JSR-303/JSR-380(例如 @NotNull, @Size, @Pattern 等)一起工作。工作原理:在控制器层(例如...

@Valid与 ParamValidator.validate

1. @Valid 的特点

  • 使用方式

    • @Valid 是 Java Bean Validation 的注解(通常结合 Hibernate Validator 使用),用于自动验证方法参数或字段的约束。

    • 配合 JSR-303/JSR-380(例如 @NotNull, @Size, @Pattern 等)一起工作。

  • 工作原理

    • 在控制器层(例如 Spring MVC 的方法参数)使用时,Spring 会自动调用 Validator 检查参数是否符合约束。

    • 如果校验失败,会抛出 ConstraintViolationExceptionMethodArgumentNotValidException

  • 适用场景

    • 控制器层请求参数的自动校验,减少手动调用校验逻辑。

    • 结合注解,简化代码逻辑。

2. ParamValidator.validate 的特点

  • 使用方式

    • ParamValidator.validate 是一种自定义方法校验工具,通常通过调用静态方法对对象进行校验,支持动态加载验证规则类或分组校验。

    • 比如:


      ParamValidator.validate(data, AddGroup.class, UpdateGroup.class);
      
  • 工作原理

    • 手动触发验证逻辑。

    • 根据传入的规则类、分组类等动态验证参数。

  • 适用场景

    • 灵活适配复杂的验证逻辑。

    • 控制器以外的场景(例如服务层或工具类)。

3. 是否重复校验的分析

  • 两者结合使用: 如果在控制器层同时使用 @ValidParamValidator.validate,可能会导致重复校验。例如:

    java
    @PostMapping("/submit")
    public ResponseEntity submit(@Valid @RequestBody MyRequest request) {
        ParamValidator.validate(request, AddGroup.class);
        return ResponseEntity.ok();
    }
    

    上述代码中,@Valid 已经自动对参数进行校验,而 ParamValidator.validate 可能重复执行类似的校验。

  • 解决方案

    1. 避免重复校验

      • 使用 @Valid 处理简单的静态校验场景。

      • 使用 ParamValidator.validate 处理动态或复杂的业务校验逻辑。

    2. 明确职责划分

      • 控制器层使用 @Valid 进行输入参数的初步校验。

      • 服务层或工具类使用 ParamValidator.validate 进行业务逻辑的深度校验。

  • 推荐设计

    • 如果 ParamValidator.validate 的规则与 @Valid 重合,可以仅选择其一,避免性能和代码冗余问题。

ParamValidator中group使用

1. ParamValidator 和 group 的使用

定义分组类

分组类通常是一个空的接口,表示不同的校验场景,例如新增和更新操作。

示例代码:定义分组类

java
public interface AddGroup {}  // 新增分组
public interface UpdateGroup {}  // 更新分组

字段校验规则与分组绑定

通过 Bean Validation 的注解(如 @NotNull@Size 等),结合 groups 属性,将字段约束绑定到具体分组。

示例代码:实体类校验规则

java
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class UserRequest {

    @NotNull(groups = AddGroup.class, message = "ID不能为空(新增场景)")
    private String id;

    @Size(groups = UpdateGroup.class, min = 5, max = 50, message = "名称长度必须在5到50之间(更新场景)")
    private String name;

    // Getters and Setters
}
  • @NotNull(groups = AddGroup.class):仅在新增操作时验证 id 字段。

  • @Size(groups = UpdateGroup.class):仅在更新操作时验证 name 字段。

调用 ParamValidator.validate

在业务逻辑中,通过 ParamValidator.validate 指定分组,动态校验不同场景的数据。

示例代码:动态调用分组校验

java
public void processRequest(UserRequest request, String operationType) {
    if ("ADD".equals(operationType)) {
        ParamValidator.validate(request, AddGroup.class);  // 新增校验
    } else if ("UPDATE".equals(operationType)) {
        ParamValidator.validate(request, UpdateGroup.class);  // 更新校验
    }
}

2. 注解 @Valid 的结合使用

在控制器层启用 @Valid

当参数使用 @Valid 注解时,可以结合 Spring MVC 的分组校验功能,在自动校验过程中指定校验分组。

示例代码:结合 @Valid 和分组

java
@PostMapping("/submit")
public ResponseEntity submit(@Validated(AddGroup.class) @RequestBody UserRequest request) {
    // 如果校验失败,Spring 会自动抛出异常
    return ResponseEntity.ok("Success");
}
  • @Validated(AddGroup.class):指定校验分组为 AddGroup

  • Spring 自动调用 Bean Validation,校验请求参数是否符合约束条件。

与 ParamValidator.validate 联合使用

在某些场景下,控制器层可以使用 @Valid 做初步校验,服务层使用 ParamValidator.validate 进一步校验复杂规则。

示例代码:结合使用

java
@PostMapping("/submit")
public ResponseEntity submit(@Validated(AddGroup.class) @RequestBody UserRequest request) {
    // 控制器层校验基础规则
    ParamValidator.validate(request, AddGroup.class);  // 服务层校验逻辑
    return ResponseEntity.ok("Success");
}
  • 控制器层使用 @Valid 处理简单规则。

  • 服务层使用 ParamValidator.validate 实现动态分组和复杂逻辑。

1. 多组的定义

可以通过 groups 属性为字段同时指定多个分组。分组的定义仍然是空接口,表示不同的校验场景。

示例:定义分组接口

java
public interface AddGroup {}    // 新增分组
public interface UpdateGroup {} // 更新分组
public interface DeleteGroup {} // 删除分组

2. 为字段指定多个分组

在字段注解的 groups 属性中,传入分组的类数组 Class<?>[],这样字段可以在多个分组中应用不同的校验规则。

示例代码:一个字段多组

java
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class UserRequest {

    @NotNull(groups = {AddGroup.class, UpdateGroup.class}, message = "ID不能为空(新增和更新)")
    private String id;

    @Size(groups = {UpdateGroup.class, DeleteGroup.class}, min = 5, max = 50, 
          message = "名称长度必须在5到50之间(更新和删除)")
    private String name;

    // Getters and Setters
}

解释:

  1. id 字段:

    • 同时在 AddGroupUpdateGroup 中校验 @NotNull

    • 当校验使用这两个分组时,都会验证 id 是否为空。

  2. name 字段:

    • UpdateGroupDeleteGroup 中校验长度范围。


3. 使用多组进行校验

当使用多个分组时,可以灵活选择分组,调用 Validator@Validated 进行校验。

(1)使用 Validator 动态校验

通过 Java 的 Validator 手动校验,并指定分组:

示例代码:动态校验多个分组

java
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.ConstraintViolation;
import java.util.Set;

public class ValidatorExample {
    public static void main(String[] args) {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();

        UserRequest request = new UserRequest();
        request.setId(null); // 演示校验失败
        request.setName("abc");

        // 针对 AddGroup 和 UpdateGroup 同时校验
        Set<ConstraintViolation<UserRequest>> violations = validator.validate(request, AddGroup.class, UpdateGroup.class);

        for (ConstraintViolation<UserRequest> violation : violations) {
            System.out.println(violation.getMessage());
        }
    }
}

输出示例:


ID不能为空(新增和更新)
名称长度必须在5到50之间(更新和删除)

(2)结合 Spring 的 @Validated

在 Spring 控制器中,可以使用 @Validated 注解指定多个分组。

示例代码:Spring 中多组校验

java
@PostMapping("/submit")
public ResponseEntity submit(@Validated({AddGroup.class, UpdateGroup.class}) @RequestBody UserRequest request) {
    return ResponseEntity.ok("校验通过!");
}

4. 注意事项

  1. 校验规则的覆盖:

    • 如果一个字段同时存在于多个分组,并且有重复的校验注解,那么所有注解规则都会生效。

    • 不同分组中相同字段的错误信息可能会叠加。

  2. 校验分组的优先级:

    • 多个分组按声明的顺序逐一校验,后续分组的校验不会覆盖前面的结果。

总结

  • 定义多个分组:通过接口定义不同的校验场景(例如新增、更新、删除)。

  • 一个字段多组校验:在字段注解中通过 groups 属性指定多个分组。

  • 灵活调用:结合 Java 原生 Validator 或 Spring 的 @Validated,动态指定分组进行校验。

  • 每个分组都会独立地进行校验

@Valid与@Validated

@Valid@Validated 都用于 Java Bean Validation 框架中的数据校验,但它们的用途和工作方式有所不同。下面是它们的详细对比:


1. @Valid

  • 来源:

    • @Valid 是 Java 的标准注解,属于 JSR-303/JSR-380(Bean Validation)的规范。

    • 它需要配合校验框架(如 Hibernate Validator)使用。

  • 作用:

    • 用于方法参数或对象字段的校验。

    • 当校验失败时,会抛出 ConstraintViolationException(通常是自动处理的)。

  • 特点:

    • 嵌套对象校验:支持对嵌套的对象进行校验。例如,校验 List 或子对象中的字段。

    • 无法指定分组@Valid 不支持分组校验,仅适用于全局验证规则。

  • 典型用法:

    java
    public class UserRequest {
        @NotNull(message = "用户名不能为空")
        private String username;
    
        @Valid // 嵌套对象校验
        private Address address;
    
        // Getters and Setters
    }
    
    @PostMapping("/submit")
    public ResponseEntity submit(@Valid @RequestBody UserRequest request) {
        // 自动校验请求参数
        return ResponseEntity.ok("校验通过");
    }
    

2. @Validated

  • 来源:

    • @Validated 是 Spring Framework 提供的注解。

    • 它扩展了 Bean Validation,并支持分组校验。

  • 作用:

    • 用于启用 Bean Validation 的功能,同时允许指定校验分组。

    • 常用于控制器层的方法参数校验,也可以应用于类级别。

  • 特点:

    • 支持分组:可以通过分组校验不同场景下的数据。例如新增和更新场景需要不同的规则。

    • 适用于 Spring:通常结合 Spring 的校验机制使用。

  • 典型用法:

    java
    public class UserRequest {
        @NotNull(groups = AddGroup.class, message = "ID不能为空")
        private String id;
    
        @Size(groups = UpdateGroup.class, min = 5, max = 50, message = "名称长度必须在5到50之间")
        private String name;
    
        // Getters and Setters
    }
    
    @PostMapping("/submit")
    public ResponseEntity submit(@Validated(AddGroup.class) @RequestBody UserRequest request) {
        // 启用分组校验,校验 AddGroup 中的规则
        return ResponseEntity.ok("校验通过");
    }
    

3. 对比总结

特性 @Valid @Validated
来源 JSR-303/Bean Validation Spring Framework
分组校验支持 不支持 支持(通过指定分组类实现)
嵌套对象校验 支持 支持
应用场景 标准的 Java 校验框架支持 Spring 项目的扩展校验功能
典型用法 简单校验(无分组、嵌套校验) 复杂校验(分组和场景区分)


4. 注意事项

  1. 嵌套校验:

    • 如果需要校验嵌套对象的字段,必须使用 @Valid@Validated 不能单独完成嵌套校验。

  2. 分组校验:

    • @Valid 不能指定分组,如果需要分组校验,必须使用 @Validated

  3. 异常处理:

    • Spring 自动处理校验失败异常(如 MethodArgumentNotValidException),可以通过 @ControllerAdvice 自定义异常处理逻辑。

两者可以结合使用的代码示例

1. 实体类定义

定义两个嵌套的类,并为字段设置分组和校验规则:

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

public class Address {
    @NotNull(message = "城市不能为空")
    private String city;

    @Size(min = 2, max = 20, message = "街道名称长度必须在2到20之间")
    private String street;

    // Getters and Setters
}

public class UserRequest {
    @NotNull(groups = AddGroup.class, message = "用户ID不能为空(新增场景)")
    private String userId;

    @Size(groups = UpdateGroup.class, min = 5, max = 50, message = "用户名长度必须在5到50之间(更新场景)")
    private String username;

    @Valid // 启用嵌套对象校验
    private Address address;

    // Getters and Setters
}

解释

  1. 嵌套对象

    • @Valid 应用于 address 字段,使得 UserRequest 的嵌套对象 Address 也会被校验。

    • 即使 @Validated 仅用于分组校验,嵌套对象校验依然由 @Valid 触发。

  2. 分组校验

    • userId 绑定到 AddGroup 分组,校验新增场景。

    • username 绑定到 UpdateGroup 分组,校验更新场景。


2. 分组接口定义

定义校验分组接口:

java
public interface AddGroup {}    // 新增场景
public interface UpdateGroup {} // 更新场景

3. 控制器中结合使用

在控制器中使用 @Validated@Valid 实现分组校验和嵌套对象校验:

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

@RestController
@RequestMapping("/users")
public class UserController {

    @PostMapping("/add")
    public ResponseEntity addUser(@Validated(AddGroup.class) @RequestBody UserRequest request) {
        // 自动校验新增场景的规则,包括嵌套的 Address 对象
        return ResponseEntity.ok("用户新增成功!");
    }

    @PostMapping("/update")
    public ResponseEntity updateUser(@Validated(UpdateGroup.class) @RequestBody UserRequest request) {
        // 自动校验更新场景的规则,包括嵌套的 Address 对象
        return ResponseEntity.ok("用户更新成功!");
    }
}

解释

  1. 分组校验

    • @Validated(AddGroup.class) 会触发 AddGroup 分组的规则。

    • @Validated(UpdateGroup.class) 会触发 UpdateGroup 分组的规则。

  2. 嵌套校验

    • 无论使用哪个分组,嵌套对象 Address 的校验规则仍然由 @Valid 自动触发。


4. 测试数据示例

  • 新增用户请求(新增场景校验触发 AddGroup

    json
    {
        "userId": null,           // 错误: 用户ID不能为空
        "username": "test",       // 不校验用户名(非 AddGroup 规则)
        "address": {
            "city": "Shanghai",   // 正确
            "street": ""          // 错误: 街道名称长度必须在2到20之间
        }
    }
    
  • 更新用户请求(更新场景校验触发 UpdateGroup

    json
    {
        "userId": null,           // 不校验用户ID(非 UpdateGroup 规则)
        "username": "t",          // 错误: 用户名长度必须在5到50之间
        "address": {
            "city": "",            // 错误: 城市不能为空
            "street": "Main Street" // 正确
        }
    }
    

5. 注意事项

  1. 嵌套校验依赖 @Valid

    • 只能通过 @Valid 触发嵌套对象的校验,即使分组校验通过,嵌套字段仍需独立处理。

  2. 分组与嵌套校验独立

    • 分组校验和嵌套校验互不冲突,分组校验只影响直接作用于实体类的字段。

  3. 结合异常处理

    • 在控制器中添加全局异常处理,返回校验失败的信息:

      java
      @ControllerAdvice
      public class GlobalExceptionHandler {
          @ExceptionHandler(MethodArgumentNotValidException.class)
          public ResponseEntity handleValidationException(MethodArgumentNotValidException ex) {
              return ResponseEntity.badRequest().body(ex.getBindingResult().getAllErrors());
          }
      }
      

总结

通过结合使用 @Valid@Validated,可以实现:

  1. 嵌套对象的校验(通过 @Valid)。

  2. 基于分组的动态校验(通过 @Validated)。

如果city也需要分组 可以实现吗?

1. 修改实体类

Address 类中,为 city 字段添加分组校验规则。

示例代码:分组校验扩展

java
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class Address {

    @NotNull(groups = {AddGroup.class, UpdateGroup.class}, message = "城市不能为空(新增和更新场景)")
    private String city;

    @Size(groups = DeleteGroup.class, min = 2, max = 20, message = "街道名称长度必须在2到20之间(删除场景)")
    private String street;

    // Getters and Setters
}

解释:

  1. city 字段:

    • 绑定到 AddGroupUpdateGroup 分组校验,新增和更新场景都会检查该字段是否为空。

  2. street 字段:

    • 绑定到 DeleteGroup 分组校验,删除场景校验街道名称长度。


2. 修改主实体类

UserRequest 中,仍然保持对嵌套对象的 @Valid 校验。

示例代码:嵌套分组校验

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

public class UserRequest {
    @NotNull(groups = AddGroup.class, message = "用户ID不能为空(新增场景)")
    private String userId;

    @Size(groups = UpdateGroup.class, min = 5, max = 50, message = "用户名长度必须在5到50之间(更新场景)")
    private String username;

    @Valid
    private Address address; // 嵌套对象校验,支持分组逻辑

    // Getters and Setters
}

嵌套字段:

  • 当使用分组校验(如 AddGroupUpdateGroup),嵌套的 Address 对象会根据分组规则验证其字段。


3. 控制器实现

使用 @Validated 指定分组,并触发对 city 的分组校验。

示例代码:控制器分组校验

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

@RestController
@RequestMapping("/users")
public class UserController {

    @PostMapping("/add")
    public ResponseEntity addUser(@Validated(AddGroup.class) @RequestBody UserRequest request) {
        // 校验新增场景的规则,包括嵌套对象 Address 的分组规则
        return ResponseEntity.ok("新增用户成功!");
    }

    @PostMapping("/update")
    public ResponseEntity updateUser(@Validated(UpdateGroup.class) @RequestBody UserRequest request) {
        // 校验更新场景的规则,包括嵌套对象 Address 的分组规则
        return ResponseEntity.ok("更新用户成功!");
    }
}

4. 测试请求示例

新增场景(触发 AddGroup

json
{
    "userId": null,            // 错误: 用户ID不能为空
    "username": "test",        // 不校验用户名(非 AddGroup 规则)
    "address": {
        "city": null,          // 错误: 城市不能为空
        "street": "Main Street" // 不校验街道(非 AddGroup 规则)
    }
}

更新场景(触发 UpdateGroup

json
{
    "userId": null,            // 不校验用户ID(非 UpdateGroup 规则)
    "username": "t",           // 错误: 用户名长度必须在5到50之间
    "address": {
        "city": "",            // 错误: 城市不能为空
        "street": "Main Street" // 不校验街道(非 UpdateGroup 规则)
    }
}

删除场景(触发 DeleteGroup

json
{
    "address": {
        "city": "Shanghai",    // 不校验城市(非 DeleteGroup 规则)
        "street": "St"         // 错误: 街道名称长度必须在2到20之间
    }
}

5. 注意事项

  1. 嵌套对象分组校验:

    • 嵌套对象需要 @Valid 注解才能触发校验,且会基于父对象指定的分组逻辑。

  2. 分组的独立性:

    • 每个分组会分别校验其绑定的规则,但不同字段的规则不会互相干扰。

  3. 性能优化:

    • 如果字段规则完全相同,可以考虑合并分组逻辑以避免重复校验。


【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。