Spring MVC学习—Validation基于注解的声明式数据校验机制全解一万字

Posted L-Java

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring MVC学习—Validation基于注解的声明式数据校验机制全解一万字相关的知识,希望对你有一定的参考价值。

  基于最新Spring 5.x,详细介绍了Spring的基于注解的声明式数据校验,包括基于javax.validation.Valid注解以及Spring的@Validated注解进行自动化数据校验的方式。

  web项目中,后端对于前端传递的参数总是免不了进行校验,比如字符长度、字段大小、null校验等等,虽然有些校验前端也会去做,但是为了增加web应用的健壮性和安全性(比如,如果绕过前端发送的直接请求,这时参数就没法得到保证了),对于重要的接口,后端进行参数二次校验是非常有必要的!
  以往的web项目中,对于参数的校验,要么是在控制器方法中通过一系列手写if语句来判断,要么是提取一个公共的校验工具类,然后手动调用工具类的方法来进行校验,对于大型项目来说,无论使用哪种方式,仍然具有不少的工作量!
  在使用Spring MVC框架之后,在进行请求的数据绑定(data binding)成功之后,可以基于javax.validation.Valid注解或者Spring的@Validated注解进行自动化数据校验,并且支持配置全局和单个请求的校验器。使用起来非常方便和简单,大大减少了开发人员的负担!

Spring MVC学习 系列文章

Spring MVC学习(1)—MVC的介绍以及Spring MVC的入门案例

Spring MVC学习(2)—Spring MVC中容器的层次结构以及父子容器的概念

Spring MVC学习(3)—Spring MVC中的核心组件以及请求的执行流程

Spring MVC学习(4)—ViewSolvsolver视图解析器的详细介绍与使用案例

Spring MVC学习(5)—基于注解的Controller控制器的配置全解【一万字】

Spring MVC学习(6)—Spring数据类型转换机制全解【一万字】

Spring MVC学习(7)—Validation基于注解的声明式数据校验机制全解【一万字】

Spring MVC学习(8)—HandlerInterceptor处理器拦截器机制全解

Spring MVC学习(9)—项目统一异常处理机制详解与使用案例

Spring MVC学习(10)—文件上传配置、DispatcherServlet的路径配置、请求和响应内容编码

Spring MVC学习(11)—跨域的介绍以及使用CORS解决跨域问题

1 Bean Validation

  Java Bean Validation 2.0后改名为Jakarta Bean Validation,2018年之后的新版本依赖均以Jakarta开头,官网是https://beanvalidation.org/,目前来说,它们的maven依赖源码基本一致,最大的区别就是最新依赖命名为jakarta.validation:jakarta.validation-api,并且最新的版本都是此依赖,以前的javax.validation旧版本依赖不再更新!
  Jakarta Bean Validation提供了一套Java Bean验证规范,也称为JSR-303规范,提出了一种声明性的Bean验证约束的功能,可以使用规范中注解帮绑定到模型属性,然后由运行时强制执行这些约束条件,还可以自定义约束注解,如下示例:

public class PersonForm {

    @NotNull
    @Size(max=64)
    private String name;

    @Min(0)
    private int age;
}

  Jakarta Bean Validation仅仅是一套规范,并没提供任何实现,但是到目前有些厂商已提供了自己的实现,我们只需要引入他们提供的maven坐标即可使用它们的实现,最常见的就是hibernate-validatorhibernate-validator和hibernate ORM框架没啥联系,唯一的联系就是它们都是由hibernate团队开发的,官网是:https://hibernate.org/validator/

1.1 内置约束注解

  hibernate-validator提供了Jakarta Bean Validation的内置约束注解的全部实现,同时可以自定义约束条件!同时,hibernate-validatorJakarta Bean Validation的内置约束注解的使用范围进行了扩展,比如@Max适用于字符串。参见:https://docs.jboss.org/hibernate/validator/7.0/reference/en-US/html_single/#section-builtin-constraints
  内置约束位于jakarta.validation.constraints包中!所有的注解都具有message、groups、payload属性:

  1. message:在违反约束时返回创建的错误消息,可以使用{key}设置默认的key,将会自动查找对应的value,如果没有对应的value,那么使用key的值作为默认错误消息。消息中可以通过{elementName}获取注解的元素值,也可以使用${validatedValue}获取注解标注的值,也可以使用其他el表达式。
  2. payload:可以将自定义有效Payload对象分配给约束,通常未使用。

  此外它们还有如下特性:

注解描述支持的类型(如果这些注解放在它们不支持的类型上,那么运行时将会抛出UnexpectedTypeException异常)
@AssertFalse检查注解的值是否为false。null值被认为验证通过。支持Boolean和boolean类型。
@AssertTrue检查注解的值是否为true。null值被认为验证通过。
@DecimalMax(value=, inclusive=)当inclusive=false,检查注解的值是否小于指定最大值,否则,检查该值是否小于或等于指定的最大值。参数value在内部会通过new BigDecimal(value)转换为的BigDecimal表现形式再进行比较。null值被认为校验通过。支持BigDecimal、BigInteger、CharSequence,byte 、short 、int 、long以及它们的包装类型的验证。请注意,由于舍入错误,因此不支持double和float。
此外hv的实现还支持Number及其子类型,以及javax.money.MonetaryAmount类型(如果存在JSR 354 的依赖)。还支持double和float,但是可能出现比较结果不准确的情况。
@DecimalMin(value=, inclusive=)当inclusive=false,检查注解的值是否大于指定最小值,否则,检查该值是否大于或等于指定的最小值。参数value在内部会通过new BigDecimal(value)转换为的BigDecimal表现形式再进行比较。null值被认为校验通过。
@Digits(integer=, fraction=)检查注解的值是否是属于最多integer整数位和fraction小数位范围内的数字。null值被认为校验通过。
@Max(value=)检查注解的值是否小于或等于指定的最大值。null值被认为校验通过。支持BigDecimal、BigInteger,byte 、short 、int 、long以及它们的包装类型的验证。请注意,由于舍入错误,不支持double和float。
此外,hv的实现还支持CharSequence及其子类型(由字符序列表示的数值计算),以及Number及其子类型以及javax.money.MonetaryAmount类型(如果存在JSR 354 的依赖)。还支持double和float,但是可能出现比较结果不准确的情况。
@Min(value=)检查注解的值是否小于或等于指定的最大值。null值被认为校验通过。
@Email检查注解的值是否是有效的电子邮件地址。可选参数 regexp 和flags允许指定电子邮件必须匹配的附加正则表达式(包括正则表达式标志)。支持CharSequence及其子类型。
@Future检查注解的值是否是一个将来的时间。null值被认为校验通过。支持java.util.Date, java.util.Calendar, java.time.Instant, java.time.LocalDate, java.time.LocalDateTime, java.time.LocalTime, java.time.MonthDay, java.time.OffsetDateTime, java.time.OffsetTime, java.time.Year, java.time.YearMonth, java.time.ZonedDateTime, java.time.chrono.HijrahDate, java.time.chrono.JapaneseDate, java.time.chrono.MinguoDate, java.time.chrono.ThaiBuddhistDate类型。
此外,hv的实现还支持Joda Time date/time类型(如果存在Joda Time依赖),以及支持ReadablePartial 和 ReadableInstant的任何实现。
@FutureOrPresent检查注解的值是否是当前时间或者将来时间。null值被认为校验通过。
@Past检查注解的值是否是一个过去的时间。null值被认为校验通过。
@PastOrPresent检查注解的值是否是当前时间或者过去时间。
@NotBlank检查注解的值是否不为null并且必须至少包含一个非空白字符。支持CharSequence及其子类型。
@NotEmpty检查注解的值是否不为null并且不为空。支持CharSequence及其子类型(计算字符序列的长度,即不能为""),以及Collection、Map、Array(至少包含一个元素/键值对)。
@NotNull检查注解的值是否不为null。支持任何类型。
@Null检查注解的值是否为null。
@Negative检查注解的值是否是严格的负数(0不通过)。null值被认为校验通过。支持BigDecimal、BigInteger,byte 、short 、int 、long 、float、double以及它们的包装类型。
此外,hv的实现还支持CharSequence及其子类型(由字符序列表示的数值计算),以及Number及其子类型以及javax.money.MonetaryAmount类型(如果存在JSR 354 的依赖)。
@NegativeOrZero检查注解的值是否是负数或0。null值被认为校验通过。null值被认为校验通过。
@Positive检查注解的值是否是严格的正数(0不通过)。null值被认为校验通过。
@PositiveOrZero检查注解的值是否是正数或0。null值被认为校验通过。
@Pattern(regex=, flags=)检查注解的值是否与给定的正则表达式匹配(regex),可以考虑给定的标记匹配项(falgs)null值被认为校验通过。支持CharSequence及其子类型。
@Positive检查注解的值是否是严格的正数(如果是0.0则通过,其他0则不通过)。null值被认为校验通过。支持BigDecimal、BigInteger,byte 、short 、int 、long 、float、double以及它们的包装类型。
此外,hv的实现还支持CharSequence及其子类型(由字符序列表示的数值计算),以及Number及其子类型以及javax.money.MonetaryAmount类型(如果存在JSR 354 的依赖)。
@PositiveOrZero检查注解的值是否是正数或0。null值被认为校验通过。
@Size(min=, max=)检查注解的值是否在给定的最小值(默认0)和最大值(默认Integer.MAX_VALUE)之间,包含两个端点值。null值被认为校验通过。支持CharSequence及其子类型(计算字符序列的长度),以及Collection、Map、Array(计算元素/键值对数量)。

1.2 其他约束注解

  除了由Jakarta Bean Validation API定义的约束注解之外,Hibernate Validator还提供了几个有用的自定义约束注解,虽然可能用不到,但是可以看看以防不时之需!

注解描述支持的类型
@CreditCardNumber(ignoreNonDigitCharacters=)检查注解的值是否通过 Luhn 校验和测试,即美国信用卡号的格式是否正确。ignoreNonDigitCharacters表示是否允许忽略非数字字符,默认值为 false。null值被认为校验通过。支持CharSequence及其子类型。
@Currency(value=)检查注解的javax.money.MonetaryAmount的货币单位是否属于包含在指定的value单位数组中。null值被认为校验通过。支持javax.money.MonetaryAmount及其子类型(如果存在JSR 354 的依赖)。
@DurationMax(days=, hours=, minutes=, seconds=, millis=, nanos=, inclusive=)检查注解的java.time.Duration表示的时间差不大于注解指定的值,如果inclusive标志设置为 true,则允许相等。null值被认为校验通过。支持java.time.Duration类型。
@DurationMin(days=, hours=, minutes=, seconds=, millis=, nanos=, inclusive=)检查注解的java.time.Duration表示的时间差不小于注解指定的值,如果inclusive标志设置为 true,则允许相等。null值被认为校验通过。
@EAN检查注解的值是否是有效的EAN(商品用条形码)。type属性指定EAN类型,默认EAN-13。null值被认为校验通过。支持CharSequence及其子类型。
@ISBN检查注解的值是否是有效的ISBN(国际标准书号)。type属性指定ISBN类型,默认ISBN-13。null值被认为校验通过。支持CharSequence及其子类型。
@Length(min=, max=)检查注解的值长度是否在最小值和最大值之间,包含两个端点值。null值被认为校验通过。支持CharSequence及其子类型。
@CodePointLength(min=, max=, normalizationStrategy=)检查注解的值的代码点长度是否在最小值和最大值之间,包含两个端点值。如果设置了normalizationStrategy,则验证规范化值。null值被认为校验通过。支持CharSequence及其子类型。
@Range(min=, max=)检查注解的值是否位于指定最小值和最大值之间,包含两个端点值。null值被认为校验通过。支持BigDecimal、BigInteger,byte 、short 、int 、long 、float、double以及它们的包装类型。
@UniqueElements检查注解的集合是否仅包含唯一元素,通过equals()方法来比较元素是否相等。null值被认为校验通过。支持Collection及其子类型。
@URL(protocol=, host=, port=, regexp=, flags=)根据 RFC2396 规范检查检查注解的值是否为有效的 URL。如果在注解中指定了任何可选参数:协议、主机或端口,则相应的 URL 片段必须与指定的值匹配。可选参数 regexp 和flags允许指定URL必须匹配附加的正则表达式(包括正则表达式标志)。默认情况下,此约束使用java.net.URL的构造器来验证给定字符串是否表示有效的 URL。支持CharSequence及其子类型。

1.3 集成hibernate-validator

  Spring支持和hibernate-validator非常方便的集成,执行Bean校验的核心就是javax.validation.Validator,Validator需要我们引入Bean Validation provider的依赖,也就是具体的Bean Validation的实现,例如hibernate-validator。在运行时,Validator依赖具体的Bean Validation的实现来进行校验。
  对于普通项目,我们需要引入hibernate-validator或者其他引入Bean校验的相关实现依赖,对于Spring Boot项目则会自动引入了相关依赖,我们无需引入!

<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-
validator -->
<!-- 引入hibernate-validator -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.18.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.el/javax.el-api -->
<!-- tomcat 7 可能需要该依赖 -->
<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.el</artifactId>
    <version>3.0.1-b08</version>
</dependency>

  注意,hibernate-validator 7的版本的Bean validation API均从javax.validation换成了jakarta.validation,因此如果切换版本,可能需要重新导入API的类路径。

1.4 配置Validator Bean

  如果我们想要配置能够在其他地方手动调用的Validator,则可以通过Spring提供的LocalValidatorFactoryBean来快速将Validator配置为Spring管理的Bean!
  如果需要手动校验,那么由于LocalValidatorFactoryBean 实现了 javax.validation.validatorFactory 和 javax.validation.Validator,以及 Spring 的org.springframework.validation.Validator,因此我们可以将对其中任一接口的引用注入需要手动调用验证逻辑的bean中。

@Bean
public LocalValidatorFactoryBean validator() {
    LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
    //即使不设置Provider,Spring也会自动查找Provider的实现
    //localValidatorFactoryBean.setProviderClass(HibernateValidator.class);
    return localValidatorFactoryBean;
}

2 Spring MVC参数属性校验

  这里的配置仅仅是针对Spring MVC的控制器方法绑定的参数对象的属性进行校验,如果需要对于参数本身以及返回值进行校验,那么需要后面的介绍!
  这里的数据校验是在数据绑定成功之后进行的,和数据绑定类似, Spring MVC 绑定参数的属性校验支持全局配置和局部配置。

2.1 全局和局部配置

  对于普通项目中Spring MVC的参数绑定校验,我们只需要通过@EnableWebMvc或者<mvc:annotation-driven/>打开MVC配置,Spring MVC会自动尝试查找类路径中引入的ValidationProvider的实现并将第一个找到的Provider配置全局的Validator。从而无需我们手动配置。如果只引入了hibernate-validator的依赖 ,那么只有一个Provider的实现——HibernateValidator。对于Spring Boot项目则会自动开启MVC配置,同样无需我们手动开启。
  如果我们需要手动指定Spring MVC使用的全局Validator,那么如下配置:

/**
 * @author lx
 */
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    /**
     * 提供自定义Validator验证器,而不是默认创建的验证器
     * 如果返回null,并且如果类路径中存在 JSR-303 Provider的实现,那么将会默认创建类型为
     * org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean的Validator
     */
    @Override
    public Validator getValidator() {
        LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
        //即使不设置Provider,Spring也会自动查找类路径中的Provider的实现
        //localValidatorFactoryBean.setProviderClass(HibernateValidator.class);
        return localValidatorFactoryBean;
    }
}

  下面的示例演示如何在 XML 中实现相同的配置:

<!--validator属性用于指定Validator bean,该属性不是必须的,除非想要自定义
validator-->
<!--如果未指定,那么自动查找类路径中的JSR-303 provider实现来配置一个类型为-->
<!--org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean的Validator-->
<mvc:annotation-driven validator="mvcValidator"/>

  当然,我们同样可以为某些请求在注册一个局部的Validator:

/**
 * 添加局部的Validator
 */
@InitBinder
public void initBinder(WebDataBinder dataBinder) {
    //如果使用预定义的Validator,那么需要手动调用afterPropertiesSet初始化
    //如果是使用自定义的Validator实现,那么需要supports方法支持要校验的参数类型否则抛出异常
    LocalValidatorFactoryBean optionalValidatorFactoryBean = new LocalValidatorFactoryBean();
    optionalValidatorFactoryBean.afterPropertiesSet();
    //可以添加多个Validator
    dataBinder.addValidators(optionalValidatorFactoryBean);
}

  如果同时添加了全局和局部的Validator,那么在校验时会形成validators链,其中全局Validator排在最前面,校验时会按照排序顺序依次调用每一个Validator进行校验,所有的Validator都通过才算通过!

2.2 测试案例

  在开启MVC配置之后,将会自动配置Spring MVC的全局Validator,我们直接进行测试!我们采用lmbok的@Data注解来省略setter、getter、toString方法,如果不会那么请手写相关方法。
  下面是一个需要校验的实体,要求id、sex和name都不能为null,id要求为正数,sex要求为0或者1,name要求在1到是个字符之间:

@Data
public class User1 {

    @Positive
    @NotNull
    private Long id;

    @Range(min = 0, max = 1)
    @NotNull
    private Byte sex;

    @Size(min = 1, max = 10)
    @NotBlank
    private String name;
}

  接下来是控制器方法,非常重要的一步就是,我们需要在对对象属性进行校验的对象参数前加上@Validated或者@Valid注解javax.validation.Valid注解可以标记在属性、方法参数或方法返回类型上以表示对标记的对象进行级联校验,也就是校验对象内部的属性,而org.springframework.validation.annotation.Validated则是Spring提供的注解,它具有和@Valid一样的功能,并且还新增了可选验证组的功能,后面会讲!

/**
 * @author lx
 */
@RestController
public class GlobalMvcValidationController {

    /**
     * 支持application/json请求的参数校验
     */
    @PostMapping("/pv1")
    @ResponseBody
    public User1 pv1(@Validated @RequestBody User1 user1) {
        System.out.println(user1);
        user1.setId(0L);
        return user1;
    }

    /**
     * 支持普通请求的参数校验
     */
    @GetMapping("/pv2")
    @ResponseBody
    public void pv2(@Valid User1 user1) {
        System.out.println(user1);
    }
}

  访问/pv3?id=1&sex=2&name=name,很明显,sex属性的值不符合要求,服务器将会返回400响应码,意思就是客户端发送的请求有误:
在这里插入图片描述
  控制台能够看到更加明确的异常信息(后面我们会讲如何把数据校验抛出的异常整合到全局异常处理中):
在这里插入图片描述
  如果访问/pv3?id=-1&sex=2&name=name,我们会收到两条错误校验不通过的数据:
在这里插入图片描述
  继续访问/pv3?id=1&sex=1&name=name,当然提交的数据是符合要求的:
在这里插入图片描述

2.3 级联校验

  在上面我们就说了@Valid支持级联校验,如果校验的对象内部的属性中同样标注了@Valid注解,那么validation engine验证引擎将会自动递归跟进!同样,级联校验的对象如果为null,那么被忽略(算作校验通过)。
  如下实体,内部具有一个对象属性,我们采用@Valid注解进行级联校验:

@Data
public class User2 {

    @Positive
    @NotNull
    private Long id;

    @Range(min = 0, max = 1)
    @NotNull
    private Byte sex;

    @Size(min = 1, max = 10)
    @NotBlank
    private String name;

    /**
     * 标注@Valid注解,对对象类型的属性进行级联校验
     */
    @Valid
    @NotNull
    private Address address;

    @Data
    public class Address {
        @NotBlank
        @Pattern(regexp = "\\\\d{6}")
        private String postcode;
        @NotBlank
        @Size(min = 10, max = 100)
        private String workAddress;
        @NotBlank
        @Size(min = 10, max = 100)
        private String homeAddress;

    }

}

  控制器方法:

/**
 * 级联校验
 */
@PostMapping("/pv3")
@ResponseBody
public User2 pv3(@RequestBody @Valid User2 user2) {
    System.out.println(user2);
    return user2;
}

  测试数据:

{
  "id": 1,
  "sex": 1,
  "name": "name",
  "address": {
    "postcode": "011111",
    "workAddress": "1111111111",
    "homeAddress": "1111111111"
  }
}

  结果当然是校验通过:
在这里插入图片描述
  将内部对象的postcode属性改为5个字符再次测试:

{
  "id": 1,
  "sex": 1,
  "name": "name",
  "address": {
    "postcode": "11111",
    "workAddress": "1111111111",
    "homeAddress": "1111111111"
  }
}

  由于postcode不符合指定的模式,级联校验不通过:
在这里插入图片描述

2.4 容器校验

  Java容器包括Collection、Map、Array等常见类型。
  @Valid同样支持容器元素的内部属性校验,任何类型的容器属性可以加上@Valid 注解,这将导致校验每个元素进行级联校验(map包括key和value)。但是hibernate validator不推荐这么做,而是希望添加在容器的具体的泛型类型上,这样的好处是校验的目的更加明确。对于数组的校验似乎有一定的限制,比如目前测试无法校验null元素。
  对容器元素进行校验时,任何元素不满足条件就算做校验不通过!
  如下实体:

@Data
public class User3 {

    /**
     * list至少包含两个元素
     * 元素不能为null且进行级联校验
     */
    @Size(min = 2)
    @NotNull
    @Valid
    private List<@NotNull InnerClass> user1s;

    /**
     * map不能为空
     * key的长度至少为2个字符且不是空白字符,value不能为null且进行级联校验
     */
    @NotEmpty
    private Map<@NotBlank @Size(min = 2) String, @NotNull @Valid InnerClass> stringUser1Map;

    /**
     * 数组不能为空,且对元素进行级联校验
     * 校验似乎有一定的限制,比如目前测试无法通过@NotNull校验null元素
     */
    @NotEmpty
    @Valid
    @NotNull
    private @NotNull InnerClass[] user2s;

    @Data
    public static class InnerClass {
        @NotNull
        @Min(1)
        private Long id;

        @NotBlank
        @Size(min = 5)
        private String name;
    }
}

  一个控制器方法:

/**
 * 容器元素校验
 */
@PostMapping("/pv4")
@ResponseBody
public User3 pv4(@RequestBody @Valid User3 user3) {
    System.out.println(user3);
    return user3;
}

  测试数据:

{
	"user1s": [{
		"id": 1,
		"name": "11111"
	}, null],
	"user2s": [{
		"id": 1,
		"name": "11111"
	}, null],
	"stringUser1Map": {
		"11": {
			"id": 1,
			"name": "11111"
		},
		"1": {
			"id": 1,
			"name": "11111"
		}
	}
}

3 Spring驱动方法校验

3.1 Spring MVC参数属性校验的局限性

  在上面 “Spring MVC参数属性校验” 的部分中,通过MVC配置的默认校验,仅仅是针对Spring MVC的控制器方法绑定的参数对象的属性进行校验,如果还需要对于方法参数本身进行或者方法的返回值进行校验,比如校验方法参数和返回值本身不为null,或者需要对属于非控制器的方法进行同样的Bean validation校验,那么我们需要配置Spring 驱动方法校验。Spring 驱动方法校验的校验规则和上面的Spring MVC参数属性校验的校验规则都是一样的,只不过它的应用范围更加广泛,在普通项目中应该手动配置Spring 驱动方法校验。同样,如果是Spring Boot的web项目,那么Spring Boot为我们自动配置好了,我们无需任何配置!
  下面,我们测试默认Spring MVC参数属性校验的局限性!
  如下GlobalValidationController控制器类,前两个方法需要接收String、Long类型的参数,实际上对于这种非自定义类型的参数,我们只能将校验的注解写在控制器方法的参数上,后两个方法虽然是我们自定义的User1类型的参数,但是第三个方法我们希望校验这个传递进来的User1对象本身不为null,对于第四个方法则是希望校验这个集合类型的参数本身的元素数量最少为2个等,这种情况下,校验注解同样只能写在控制器方法的参数上!

/**
 * @author lx
 */
@RestController
public class GlobalValidationController {

    /**
     * str参数不能是空字符串,且长度为2到5个字符
     */
    @GetMapping("/pv5")
    @ResponseBody
    public String pv5(@Valid @Size(min = 2, max = 5) @NotBlank String str) {
        System.out.println(str);
        return str;
    }

    /**
     * id参数不能为null,且最小值为333
     */
    @GetMapping("/pv6/{id}")
    @ResponseBody
    public Long pv6(@PathVariable @Valid @Min(333) @NotNull Long id) {
        System.out.println(id);
        return id;
    }

    /**
     * User1参数本身不能为null
     */
    @PostMapping("/pv7")
    @ResponseBody
    public User1 pv7(@RequestBody(required = false) @Valid @NotNull User1 user1) {
        System.out.println(user1);
        return user1;
    }

    /**
     * List<User1>参数本身不能为null,且至少包括两个元素,且内部元素不能为null
     */
    @PostMapping("/pv8")
    @ResponseBody
    public List<User1> pv7(@RequestBody @Valid &

以上是关于Spring MVC学习—Validation基于注解的声明式数据校验机制全解一万字的主要内容,如果未能解决你的问题,请参考以下文章

JSR-303 Bean Validation 介绍及 Spring MVC 服务端验证最佳实践

JSR-303 Bean Validation 介绍及 Spring MVC 服务端验证最佳实践

覆盖spring web mvc的默认Hibernate Validator

Spring MVC 3 验证 - 找不到默认提供程序

Spring MVC学习—MVC的介绍以及Spring MVC的入门案例

Java / Spring MVC 3 验证电子邮件地址