admin管理员组

文章数量:1794759

SpringBoot使用注解校验参数

SpringBoot使用注解校验参数

文章目录
  • 内置注解参数校验
    • 引入依赖
    • 常用注解
    • 使用方式
    • @Validated和@Valid
    • 嵌套校验
  • 自定义注解参数校验
    • 编写自定义注解
    • 实现注解校验类
    • 使用自定义注解
    • 自定义注解属性
  • 分组校验
    • 定义分组接口
    • 校验参数注解上指定分组类
    • Controller层添加@Validated注解并指定分组类

内置注解参数校验 引入依赖

SpringBoot 2.3之前的版本只需要引入web,之后的版本还需要引入validation组件。

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> 常用注解 注解说明
@AssertFalse值必须为false或null
@AssertTrue值必须为true或null
@DecimalMax必须为数字,且不能超过指定的最大值
@DecimalMin必须为数字,且不能小于指定的最小值
@Digits必须为数字,且位数必须在指定范围内
@Email值必须符合email格式
@Max必须不大于指定的最大值
@Min必须不小于指定的最小值
@NotNull值不能为null
@NotBlank只适用于字符串,值不能为null,且字符串使用trim()后也不能为空
@NotEmpty值不能为null,字符串不能为空,数组、集合、map等大小不能为0
@Pattern值必须满足指定的正则表达式
@Positive值必须为正整数
@Size元素大小必须在指定范围内
使用方式

校验的注解有@Validated或@Valid,常见的校验的场景有以下两种:

  • 校验实体结构中的字段

    @Data public class User { @NotEmpty(message = "user name cannot be null or empty.") private String name; @Positive(message = "user age must larger than zero.") private int age; @Email(message = "email format must be correct.") private String email; }
  • 在Controller层直接校验请求参数

    @GetMapping("/demo/users/{name}") public User getUserByName(@PathVariable(value = "name") @Size(min = 1, max = 20) String name) { return userService.getUserByName(name); }

要使得上述注解生效,只需要在Controller层做如下处理:

  • 对于实体类User的校验,可以直接在Controller类上面添加@Validated注解,或者方法中字段前添加@Validated或@Valid注解。

    //类和方法上只要有一个地方使用@Validated即可 //@Validated @RestController public class UserController { @Autowired private UserService userService; //方法上也可以使用@Validated @PostMapping(value = "/demo/users") public void createUser(@RequestBody @Valid User user) { userService.createUser(user); } }

    此外,@Validated和@Valid注解也可以放在UserService的接口中生效。

  • 对于Controller层请求参数的校验,需在Controller类上添加@Validated注解。

    @Validated @RestController public class UserController { @Autowired private UserService userService; @GetMapping("/demo/users/{name}") public User getUserByName(@PathVariable(value = "name") @Size(min = 3, max = 4) String name) { return userService.getUserByName(name); } }
@Validated和@Valid

这两个注解都可以使参数校验生效,但也有些区别:

区别@Validated@Valid
依赖org.springframework.validation.annotation.Validatedjavax.validation.Valid
作用目标作用于类、方法和参数上,不能用于字段上作用于方法、字段、构造函数、参数上
分组校验支持不支持
嵌套校验不支持单独的嵌套校验,但可以和@Valid配合使用(@Valid用于字段上)不支持单独的嵌套校验,但支持和@Valid或@Validated配合使用
嵌套校验

@Validated和@Valid注解都不具备单独进行嵌套校验的功能。

所谓的嵌套校验,就是需要校验某个实体类中定义的其他实体。例如,假设User类中定义了一个Family实体类的成员变量family,则需要在family字段上面添加@Valid注解才能使对family的校验生效。

@Data public class User { @NotEmpty(message = "user name cannot be null or empty.") private String name; @Positive(message = "user age must larger than zero.") private int age; @Email(message = "email format must be correct.") private String email; //添加@Valid注解才能使Family实体中对address的校验注解生效 @Valid private Family family; } @Data public class Family { @NotBlank(message = "address must be not blank.") private String address; private int memberNum; }

这样,只要在Controller层对User类参数添加@Validated或@Valid注解即可。

自定义注解参数校验

当内置的注解无法满足对参数的校验场景时,可以自定义注解进行参数校验。实现自定义注解参数校验通常分为以下三步:

​ 1)编写自定义注解

​ 2)实现注解校验类

​ 3)使用自定义注解

编写自定义注解

假设现在需要校验一个User类用户是否已成年,可以自定义一个这样的校验注解:

@Documented @Target({TYPE, CONSTRUCTOR, PARAMETER}) @Retention(RUNTIME) @Constraint(validatedBy = {AdultUserValidator.class}) public @interface AdultUserValidation { String message() default ""; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; @Target({TYPE, CONSTRUCTOR, PARAMETER}) @Retention(RUNTIME) @Documented public @interface List { AdultUserValidation[] value(); } }

相关注解介绍:

注解用法说明备注
@Documented被此注解修饰的类,在生成文档时,会显示该类的内容
@Target通过ElementType指定该注解可以作用哪些目标元素上ElementType介绍详见下文
@Retention保留注解的位置,由RetentionPolicy指定RetentionPolicy介绍详见下文
@Constraint用于限定自定义注解的使用范围,通过validatedBy指定实现注解校验的类

ElementType介绍:

ElementType是一个枚举类型,配合@Target注解用于指定注解可以使用在哪些目标元素上,具体枚举选项如下:

选项说明
ElementType.TYPE该注解可以作用在类、接口、枚举上
ElementType.FIELD该注解只能作用在属性字段上
ElementType.METHOD该注解只能作用在方法上
ElementType.PARAMETER该注解只能作用在方法的参数上
ElementType.CONSTRUCTOR该注解只能作用在类的构造器上
ElementType.LOCAL_VARIABLE该注解只能作用在局部变量上
ElementType.ANNOTATION_TYPE该注解只能作用在注解上
ElementType.PACKAGE该注解只能作用在包上
ElementType.TYPE_PARAMETER该注解可以作用在类的参数上
ElementType.TYPE_USE该注解可以作用在类的任何语句上

RetentionPolicy介绍:

通常,注解的生命周期分为三个阶段:Java源文件 -> class文件 -> 内存中的字节码,这三个阶段分别可以通过RetentionPolicy的三个枚举类型指定,配合@Retention注解指定注解保留在什么地方,具体枚举选项如下:

选项说明
RetentionPolicy.SOURCE注解只被保留在源文件阶段,编译时会被丢弃
RetentionPolicy.CLASS注解被保留在class文件阶段,但不会被加载到JVM中
RetentionPolicy.RUNTIME注解被保留在源文件、class文件中,运行时会被加载到JVM中
实现注解校验类

实现@AdultUserValidation注解中的AdultUserValidator类如下:

public class AdultUserValidator implements ConstraintValidator<AdultUserValidation, User> { @Override public void initialize(AdultUserValidation constraintAnnotation) { //Do some initialization work before validation. } @Override public boolean isValid(User user, ConstraintValidatorContext context) { if (user == null) { return false; } return isAdultUser(user); } public boolean isAdultUser(User user) { return !user.getName().isEmpty() && user.getAge() >= 18; } } 使用自定义注解

自定义校验注解的使用和内置注解的使用方式基本一致,在需要使用自定义注解的地方添加该注解,然后再Controller层添加@Validated或@Valid注解使其生效。

通常,对字段的校验需要在自定义注解的@Target中添加FIELD,然后在对应的字段上添加该注解;对实体类(例如User类)整体进行校验的注解会在注解的@Target中添加TYPE,然后就可以在User类前面添加自定义的注解

@AdultUserValidation public class User { ... } 自定义注解属性

在定义注解时,还可以根据需要来定义属性来实现特定的校验场景。例如,假设在User类中定义了一个gender字段来指定用户性别,gender字段只有Male和Female两个可选字段。

自定义一个@GenderValidation注解来校验gender字段,注解定义如下:

@Documented @Target({FIELD, PARAMETER}) @Retention(RUNTIME) @Constraint(validatedBy = {GenderValidator.class}) public @interface GenderValidation { String message() default "Gender must be Male or Female."; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; //自定义属性 //校验字段的值必须在注解中指定的数组中才能校验通过 String[] value() default {}; @Documented @Target({TYPE, CONSTRUCTOR, PARAMETER}) @Retention(RUNTIME) public @interface List { GenderValidation[] value(); } }

注解校验类GenderValidator如下:

public class GenderValidator implements ConstraintValidator<GenderValidation, String> { private List<String> genders; @Override public void initialize(GenderValidation constraintAnnotation) { this.genders = Arrays.asList(constraintAnnotation.value()); } @Override public boolean isValid(String value, ConstraintValidatorContext context) { return genders.contains(value); } }

然后,可以在User类的gender字段上添加注解@GenderValidation,如下所示:

@Data public class User { ... //value属性指定gender的可选项只能为"Male"和"Female",否则校验不通过 @GenderValidation(value = {"Male", "Female"}, message = "user gender must be Male or Female.") private String gender; } 分组校验

@Validated和@Valid注解的区别之一就是前者支持分组校验。所谓的分组校验,可以理解为按照不同的分组或场景分别对相应的参数进行校验。要实现分组校验,可以分以下三步进行:

​ 1)定义分组接口或类

​ 2)在待校验参数的注解上指定分组类

​ 3)在Controller层添加@Validated注解并指定分组类

定义分组接口

定义以下两个分组校验接口,分别用于创建用户和更新用户时的校验。

public interface Create extends Default { } public interface Update extends Default { }

这里继承了javax.validation.groups.Default接口,表示在进行Create/Update分组校验时,其他使用了javax.validation的Default分组的注解的地方也会生效

校验参数注解上指定分组类

User类的修改如下,参数name字段添加Create.class分组,参数email字段添加Update.class分组,通过注解的属性groups来指定。

@Data public class User { @NotEmpty(message = "user name cannot be null or empty.", groups = Create.class) private String name; @Positive(message = "user age must larger than zero.") private int age; @Email(message = "email format must be correct.", groups = Update.class) private String email; } Controller层添加@Validated注解并指定分组类 @RestController public class UserController { @Autowired private UserService userService; @PostMapping(value = "/demo/users") public void createUser(@RequestBody @Validated(Create.class) User user) { userService.createUser(user); } @PutMapping(value = "/demo/users") public void updateUser(@RequestBody @Validated(Update.class) User user) { userService.updateUser(user); } }

至此,可以通过调用创建用户和更新用户接口进行测试。例如,通过验证可知,在调用createUser时,email字段不做校验,即不按照email格式仍然可以创建成功,而在调用updateUser时,该字段校验生效。

本文标签: 注解参数SpringBoot