springboot Validated失效的问题及解决思路

 更新时间:2026年01月05日 10:00:00   作者:胡萝卜的兔  
文章主要介绍了Java Bean Validation(JSR 303/JSR 349)和Hibernate Validator的基本用法,包括常用注解的使用、@Valid和@Validated注解的区别、如何自定义校验注解以及如何在Spring Boot中使用这些校验机制

1、 pom 加入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

2、实体类加入注解

@Data
@Accessors(chain = true)
@TableName("user")
@Validated
public class User {
    private Integer id;
    @NotBlank(message = "姓名不能为空")
    private  String name;
    @Min(message = "年龄不正确1-100之间",value = 1)
    @Max(message = "年龄不正确1-100之间",value = 1)

    private  Integer age ;
    private Integer sex;

}

3、控制器加入@Valid注解

4、测试效果

常见的注解

通常情况下,在javax.validation.constraints 包下,定义了一系列的约束(constraint)注解,一共 22 个注解,快速略过即可。如下:

空和非空检查

  • @NotBlank:只能用于字符串不为 null ,并且字符串 .trim() 以后 length 要大于 0 。
  • @NotEmpty:集合对象的元素不为 0 ,即集合不为空 。
  • @NotNull:不能为 null 。
  • @Null`:必须为 null 。

数值检查

  • @DecimalMax(value):被注释的元素必须是一个数字,其值必须小于等于指定的最大值。 
  • @DecimalMin(value):被注释的元素必须是一个数字,其值必须大于等于指定的最小值。 
  • @Digits(integer, fraction):被注释的元素必须是一个数字,其值必须在可接受的范围内。 
  • @Positive:判断正数。 
  • @PositiveOrZero:判断正数或 0 。 
  • @Max(value):该字段的值只能小于或等于该值。 
  • @Min(value):该字段的值只能大于或等于该值。 
  • @Negative:判断负数。 
  • @NegativeOrZero:判断负数或 0 。

Boolean 值检查

  • @AssertFalse:被注释的元素必须为 true 。@AssertTrue:被注释的元素必须为 false 。

长度检查

  • @Size(max, min):检查字段的 size 是否在 min 和 max 之间,可以是字符串、数组、集合、Map 等。

日期检查

  • @Future:被注释的元素必须是一个将来的日期。
  • @FutureOrPresent:判断日期是否是将来或现在日期。
  • @Past:检查该字段的日期是在过去。
  • @PastOrPresent:判断日期是否是过去或现在日期。

其它检查

  • @Email:被注释的元素必须是电子邮箱地址。
  • @Pattern(value):被注释的元素必须符合指定的正则表达式。

Hibernate Validator 附加的约束注解,在org.hibernate.validator.constraints 包下,定义了一系列的约束(constraint)注解。常见的如示。

  • @Range(min=, max=):被注释的元素必须在合适的范围内。
  • @Length(min=, max=):被注释的字符串的大小必须在指定的范围内。
  • @URL(protocol=,host=,port=,regexp=,flags=):被注释的字符串必须是一个有效的 URL 。
  • @SafeHtml:判断提交的 HTML 是否安全。例如说,不能包含 javascript 脚本等等。

其他的就不一一列举了,有感兴趣的小伙伴可以去源码包看看。

@Valid 和 @Validated

@Valid 注解,是 Bean Validation 所定义,可以添加在普通方法、构造方法、方法参数、方法返回、成员变量上,表示它们需要进行约束校验。

@Validated 注解,是 Spring Validation 锁定义,可以添加在类、方法参数、普通方法上,表示它们需要进行约束校验。同时,@Validated 有 value 属性,支持分组校验。

对于初学者来说,很容易搞混 @Valid 和 @Validated 注解。

  • ① 声明式校验:Spring Validation 仅对 @Validated 注解,实现声明式校验。
  • ② 分组校验:Bean Validation 提供的 @Valid注解,因为没有分组校验的属性,所以无法提供分组校验。此时,我们只能使用 @Validated 注解。
  • ③ 嵌套校验:相比来说,@Valid注解的地方,多了【成员变量】。这就导致,如果有嵌套对象的时候,只能使用@Valid注解。

自定义校验注解

虽然 JSR303 和 Hibernate Validtor 已经提供了很多校验注解,但是当面对复杂参数校验时,还是不能满足我们的要求,这时候我们就需要 自定义校验注解。

下面以“List数组中不能含有null元素”为实例自定义校验注解

1、注解定义如示。

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = ListNotHaveNullValidatorImpl.class)//此处指定了注解的实现类
public @interface ListNotHaveNull {
    /**
     * 添加value属性,可以作为校验时的条件,若不需要,可去掉此处定义
     */
    int value() default 0;
    String message() default "List集合中不能含有null元素";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    /**
     * 定义List,为了让Bean的一个属性上可以添加多套规则
     */
    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
    @Retention(RUNTIME)
    @Documented
    @interface List {
        ListNotHaveNull[] value();
    }
}

2、编写自定义校验实现类

@Service
public class ListNotHaveNullValidatorImpl implements ConstraintValidator<ListNotHaveNull, List> {
    private int value;
    @Override
    public void initialize(ListNotHaveNull constraintAnnotation) {
        //传入value 值,可以在校验中使用
        this.value = constraintAnnotation.value();
    }
    public boolean isValid(List list, ConstraintValidatorContext constraintValidatorContext) {
        for (Object object : list) {
            if (object == null) {
                //如果List集合中含有Null元素,校验失败
                return false;
            } else if (object instanceof String) {
                String value = object.toString();
                if (value.trim().length() == 0){
                    return false;
                }
            }
        }
        return true;
    }
}

3、model中添加注解:

@Data
public class SysRole {

    private Long roleId;

    @NotBlank(message = "角色名不能为空")
    private String name;

    @NotEmpty(message = "资源列表不能为空")
    @ListNotHaveNull(message = "List 中不能含有null元素")
    @Valid
    private List<String> paths;

}

4、编写前端控制器

    @PostMapping("/addRole")
    public R addRole(@RequestBody @Valid SysRole sysRole) {
        System.out.println("走到这里说明校验成功");
        System.out.println(sysRole);
        return R.ok(R.SUCCESS_MSG);
    }

使用方法同 “简单校验”,在在需要校验的Model上面加上@Valid即可。

通用的Validtor校验工具类

public class ValidatorUtils {
    private ValidatorUtils() { }

    private static Validator validator;
    static {
        validator = Validation.buildDefaultValidatorFactory().getValidator();
    }

    /**
     * 校验对象
     *
     * @param object 待校验对象
     * @param groups 待校验的组
     * @throws RRException 校验不通过,则报RRException异常
     */
    public static void validateEntity(Object object, Class<?>... groups) throws RRException {
        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);

        if (!constraintViolations.isEmpty()) {
            Iterator<ConstraintViolation<Object>> iterator = constraintViolations.iterator();
            StringBuilder msg = new StringBuilder();
            while (iterator.hasNext()) {
                ConstraintViolation<Object> constraint = iterator.next();
                msg.append(constraint.getMessage()).append(',');
            }
            throw new RRException(msg.toString().substring(0,msg.toString().lastIndexOf(',')));
        }
    }
}

使用方式,在接收到前端传递的参数后,使用ValidatorUtils.validateEntity(【参数名】);即可校验,支持分组校验,分组需要定义分组接口。

springboot2 @RequestBody @Valid校验失效问题

这是因为 @RequestBody @Valid 抛出的异常没有被捕获,所以需要建立一个全局自定义异常处理类

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseBody
    public Result MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e)
    {
        //获取实体类定义的校验注解字段上的message作为异常信息,@NotBlank(message = "用户密码不能为空!")异常信息即为"用户密码不能为空!"
        List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
        String message = allErrors.stream().map(s -> s.getDefaultMessage()).collect(Collectors.joining(";"));
        List<String> msgList = new ArrayList<>();
        msgList = Arrays.asList(message.split(";"));
        return Result.failed(msgList.get(0));
    }
}

测试

校验GET请求

这种情况下,使用@Valid和@Validated都可以。

requestParam/PathVariable参数校验

GET请求一般会使用requestParamPathVariable传参。如果参数比较多(比如超过6个),还是推荐使用DTO对象接收。否则,推荐将一个个参数平铺到方法入参中。在这种情况下,必须在Controller类上标注@Validated注解,并在入参上声明约束注解(如@Min等)。如果校验失败,会抛出ConstraintViolationException异常。代码示例如下:

校验GET必要条件

1、Controller类上标注@Validated注解

2、全局捕获该异常

    @ExceptionHandler({ConstraintViolationException.class})
    @ResponseBody
    public Result handleConstraintViolationException(ConstraintViolationException ex) {
        return Result.failed(ex.getMessage());
    }

3、如何GET请求requestParam 则@RequestParam(value = “customerId”) 不能添加该注解

4、如何GET请求PathVariable 则需加上@PathVariable()

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • JDK8通过Stream 对List,Map操作和互转的实现

    JDK8通过Stream 对List,Map操作和互转的实现

    这篇文章主要介绍了JDK8通过Stream 对List,Map操作和互转的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-09-09
  • Spring Security OAuth2 token权限隔离实例解析

    Spring Security OAuth2 token权限隔离实例解析

    这篇文章主要介绍了Spring Security OAuth2 token权限隔离实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • SpringBoot Application事件监听的实现方案

    SpringBoot Application事件监听的实现方案

    这篇文章主要介绍了SpringBoot Application事件监听的实现方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05
  • 浅谈javaSE 面向对象(Object类toString)

    浅谈javaSE 面向对象(Object类toString)

    下面小编就为大家带来一篇浅谈javaSE 面向对象(Object类toString)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06
  • ssm项目实现用户登陆持久化(token)

    ssm项目实现用户登陆持久化(token)

    这篇文章主要介绍了ssm项目实现用户登陆持久化(token),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • 浅谈Java中的桥接方法与泛型的逆变和协变

    浅谈Java中的桥接方法与泛型的逆变和协变

    对应于Java当中,协变对应的就是<? extends XXX>,而逆变对应的就是<? super XXX>,本文详细的介绍了Java中的桥接方法与泛型的逆变和协变,感兴趣的可以了解一下
    2022-04-04
  • IntelliJ IDEA2020.1 Mac maven sdk 全局配置

    IntelliJ IDEA2020.1 Mac maven sdk 全局配置

    这篇文章主要介绍了IntelliJ IDEA2020.1 Mac maven sdk 全局配置,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • JAVA使用ElasticSearch查询in和not in的实现方式

    JAVA使用ElasticSearch查询in和not in的实现方式

    今天小编就为大家分享一篇关于JAVA使用Elasticsearch查询in和not in的实现方式,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • Java多线程 两阶段终止模式Two-Phase Termination Patter

    Java多线程 两阶段终止模式Two-Phase Termination Patter

    这篇文章主要介绍了Java多线程 两阶段终止模式Two-Phase Termination Patter,该模式有两个角色,分别是Terminator,终止者,负责接收终止请求,执行终止处理,处理完成后再终止自己。TerminationRequester终止请求发出者,用来向Terminator发出终止请求,需要的朋友可以参考一下
    2021-10-10
  • Java线程池实现原理总结

    Java线程池实现原理总结

    这篇文章主要给大家分享的是Java线程池实现原理总结,线程池参数、线程池执行流程等内容上总结,具有一定参考戒指,需要的小伙伴可以参考一下,希望对你有所帮助
    2022-01-01

最新评论