@Valid校验 -(Spring 默认不支持直接校验 List<@Valid Entity>,需用包装类或手动校验。)

一、在 Spring Boot 中,直接对 List<MyEntity>使用 @Valid验证时,集合内元素的校验可能不会自动触发。

@PostMapping("/save")

public ResponseEntity<?> saveEntities(

@RequestBody @Valid List<@Valid MyEntity> entities) {

// 业务逻辑

} MyEntity中某个参数没有传入,也没有提示,参数有加@NotNull注解

在 Spring Boot 中,直接对 List<MyEntity>使用 @Valid验证时,集合内元素的校验可能不会自动触发。以下是解决方案和步骤:

1. ​​使用包装类封装集合​

创建一个包装类来包裹 List<MyEntity>,并在字段上添加 @Valid

复制代码
public class MyEntityListWrapper {
    @Valid
    private List<MyEntity> entities;

    // Getter 和 Setter
}

修改 Controller 方法:

复制代码
@PostMapping("/save")
public ResponseEntity<?> saveEntities(
    @RequestBody @Valid MyEntityListWrapper wrapper) {
    List<MyEntity> entities = wrapper.getEntities();
    // 业务逻辑
}

3. ​​确保依赖正确​

检查 pom.xml是否包含验证依赖:

复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

4. ​​验证 MyEntity配置​

确认实体类字段有 @NotNull注解:

复制代码
public class MyEntity {
    @NotNull(message = "字段不能为空")
    private Double requiredField;
    // 其他字段及 Getter/Setter
}

关键点说明:

  • ​集合内元素验证​ ​:Spring 默认不支持直接校验 List<@Valid Entity>,需用包装类或手动校验。

  • ​错误提示​​:包装类方式会自动返回标准错误响应(HTTP 400 + 错误详情)。

  • ​测试请求​ ​:确保 POST 请求的 Body 是 JSON 数组,且缺失了标记为 @NotNull的字段。

二、@Valid校验数据类型

@Valid可以校验数据类型​ ​,但需要配合 JSR 380 (Bean Validation 2.0) 的注解一起使用。@Valid本身是一个标记注解,它的作用是​​触发​​对对象属性的验证,而实际的类型校验是通过其他约束注解实现的。

数据类型校验的核心机制

1. 常用数据类型校验注解

这些注解放在实体类的字段上,配合 @Valid使用:

注解 功能描述 示例
@NotNull 值不能为 null @NotNull private String name;
@NotEmpty 字符串/集合不能为空 @NotEmpty private List<String> items;
@NotBlank 字符串不能为空且必须包含非空白字符 @NotBlank private String username;
@Size(min, max) 长度/大小范围限制 @Size(min=2, max=10) private String code;
@Min(value) 数值最小值限制 @Min(18) private Integer age;
@Max(value) 数值最大值限制 @Max(100) private Integer score;
@DecimalMin(value) 小数最小值限制 @DecimalMin("0.01") private BigDecimal price;
@DecimalMax(value) 小数最大值限制 @DecimalMax("9999.99") private BigDecimal amount;
@Digits(integer, fraction) 整数和小数部分位数限制 @Digits(integer=4, fraction=2) private Double value;
@Pattern(regexp) 正则表达式匹配 @Pattern(regexp="[A-Za-z0-9]+") private String id;
@Email 邮箱格式验证 @Email private String email;

2. 使用示例

复制代码
public class User {
    @NotNull(message = "ID不能为空")
    private Long id;
    
    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度必须在3-20之间")
    private String username;
    
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @Min(value = 18, message = "年龄必须大于等于18")
    @Max(value = 100, message = "年龄必须小于等于100")
    private Integer age;
    
    @DecimalMin(value = "0.0", message = "余额不能为负数")
    private BigDecimal balance;
    
    // getters and setters
}

在 Controller 中使用 @Valid触发校验:

复制代码
@PostMapping("/users")
public ResponseEntity<?> createUser(@RequestBody @Valid User user) {
    // 如果校验失败,此方法不会执行
    userService.save(user);
    return ResponseEntity.ok("用户创建成功");
}

数据类型校验的工作原理

  1. ​请求到达​ ​:客户端发送 POST 请求到 /users

  2. ​参数绑定​ ​:Spring 将 JSON 数据绑定到 User对象

  3. ​校验触发​ ​:@Valid注解触发 Bean Validation

  4. ​约束检查​​:Hibernate Validator 检查所有约束注解

  5. ​结果处理​​:

    • 如果校验通过:执行方法体

    • 如果校验失败:抛出 MethodArgumentNotValidException

处理校验错误

添加全局异常处理器:

复制代码
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handleValidationExceptions(
            MethodArgumentNotValidException ex) {
        
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors().forEach(error -> {
            String fieldName = error.getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errors);
    }
}

特殊数据类型处理

1. 枚举类型校验

复制代码
public class Product {
    @NotNull(message = "产品类型不能为空")
    private ProductType type;
}

public enum ProductType {
    ELECTRONICS, CLOTHING, FOOD
}

2. 日期类型校验

复制代码
public class Event {
    @Future(message = "开始时间必须是未来时间")
    private LocalDateTime startTime;
    
    @Past(message = "创建时间必须是过去时间")
    private LocalDateTime createdAt;
}

3. 嵌套对象校验

复制代码
public class Order {
    @Valid // 触发嵌套对象的校验
    @NotNull(message = "客户信息不能为空")
    private Customer customer;
    
    @Valid
    @NotEmpty(message = "订单项不能为空")
    private List<OrderItem> items;
}

public class Customer {
    @NotBlank
    private String name;
    
    @Email
    private String email;
}

自定义数据类型校验

创建自定义校验注解:

复制代码
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneNumberValidator.class)
public @interface ValidPhoneNumber {
    String message() default "无效的手机号码";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

实现校验逻辑:

复制代码
public class PhoneNumberValidator implements ConstraintValidator<ValidPhoneNumber, String> {
    
    private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
    
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return false;
        }
        return PHONE_PATTERN.matcher(value).matches();
    }
}

在实体类中使用:

复制代码
public class Contact {
    @ValidPhoneNumber
    private String phone;
}

常见问题解答

​Q: @Valid能校验基本类型吗?​

A: 可以,但需要使用包装类型(如 Integer代替 int),因为基本类型有默认值(0),无法区分 null 和 0。

​Q: 如何校验集合中的元素?​

A: 使用 List<@Valid MyObject>语法或创建包装类(如前面的回答所示)。

​Q: 校验失败时如何自定义响应?​

A: 通过 @ExceptionHandler捕获 MethodArgumentNotValidException并构造自定义响应。

​Q: 如何禁用某些校验规则?​

A: 使用验证组(Validation Groups):

复制代码
public interface BasicValidation {}
public interface FullValidation extends BasicValidation {}

public class User {
    @NotNull(groups = BasicValidation.class)
    private String username;
    
    @Email(groups = FullValidation.class)
    private String email;
}

// 在Controller中指定组
@PostMapping
public ResponseEntity<?> create(@Validated(BasicValidation.class) @RequestBody User user)

@Valid配合 Bean Validation 注解可以:

  • ✅ 校验各种数据类型(字符串、数字、日期、枚举等)

  • ✅ 验证值的范围、格式、长度等约束

  • ✅ 处理嵌套对象和集合的校验

  • ✅ 支持自定义校验规则

  • ✅ 提供清晰的错误信息

通过合理使用这些功能,可以确保应用程序接收到的数据符合预期的格式和业务规则。

相关推荐
汤姆yu5 小时前
2026版基于python的协同过滤音乐推荐系统
开发语言·python
汤姆yu5 小时前
基于python的电子商务管理系统
开发语言·python
我是大咖5 小时前
C语言-贪吃蛇项目开发工具篇---ncursee库安装
c语言·开发语言
雨夜之寂5 小时前
mcp java实战 第一章-第一节-MCP协议简介.md
java·后端
皮皮林5515 小时前
蚂蚁又开源了一个顶级 Java 项目!
java
吹晚风吧6 小时前
spring是如何解决循环依赖的(二级缓存不行吗)?
java·spring·循环依赖·三级缓存
九丶弟6 小时前
SpringBoot的cache使用说明
java·spring boot·spring·cache
weixin_445476686 小时前
Java并发编程——synchronized的实现原理与应用
java·开发语言·并发·synchronized
yi碗汤园6 小时前
【超详细】C#自定义工具类-StringHelper
开发语言·前端·unity·c#·游戏引擎