admin管理员组文章数量:1794759
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格式 | |
@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); } }
这两个注解都可以使参数校验生效,但也有些区别:
依赖 | org.springframework.validation.annotation.Validated | javax.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
版权声明:本文标题:SpringBoot使用注解校验参数 内容由林淑君副主任自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.xiehuijuan.com/baike/1686808079a105501.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论