文章首发于有间博客,欢迎大伙光临! 自定义注解校验–ConstraintValidator
我们在进行接口编写的时候,往往需要对VO传入的参数进行一个校验。但在业务逻辑中每次都对参数进行校验显得复杂且多余,如果校验参数较多使用AOP会显得逻辑杂乱,所以我们往往使用注解的方式对传入的参数进行一个格式的校验,但已有的注解不是万能的,我们在实现当前业务逻辑判断时会遇到已有注解不能校验的逻辑,我们则需要自己自定义校验注解进行对参数的校验。
创建项目
前期工作:创建一个maven的项目,传入Springboot的依赖,建立实体与控制类,进行调用即可。
依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
主启动类
@SpringBootApplication
public class ValidatorTestApplication {
public static void main(String[] args) {
SpringApplication.run(ValidatorTestApplication.class);
}
}
实体参考类
public class User {
private Long userId;
private String username;
private String tel;
private Integer level;
public User(Long userId, String username, String tel, Integer level) {
this.userId = userId;
this.username = username;
this.tel = tel;
this.level = level;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public Integer getLevel() {
return level;
}
public void setLevel(Integer level) {
this.level = level;
}
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", username='" + username + '\'' +
", tel='" + tel + '\'' +
", level=" + level +
'}';
}
}
Controller层
@RestController
public class UserController {
@PostMapping(value = "/register")
public String register(User user){
System.out.println(user);
return "success to register";
}
}
无注解测试
可以看到在没有加上校验时是能够正常传入。
上注解
假如现在提出一个需求,在上述的前提下,level字段值允许接收偶数的类型,我们使用自定义注解的方式来完成。
定义注解
定义一个注解,由于要使用ConstraintValidator进行校验,groups 和 payload这两个参数是必要的。groups可以指定注解使用的场景,一个实体类可能会在多个场合有使用,如插入,删除等。通过groups可以指定该注解在插入/删除的环境下生效。payload往往对bean进行使用。
@Target(ElementType.FIELD)
@Documented
@Retention(value = RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {
EvenValidator.class})
public @interface EvenNumber {
//如果出错,返回的数据
String message() default "输入的等级必须为偶数";
Class<?>[] groups() default {
};
Class<? extends Payload>[] payload() default {
};
}
定义注解约束
注解约束实现了ConstraintValidator接口,对应传入的是注解和需要判断的类型。
public class EvenValidator implements ConstraintValidator<EvenNumber, Integer> {
@Override //初始化传入的是注解
public void initialize(EvenNumber constraintAnnotation) {
}
//进行校验的逻辑判断
@Override
public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) {
if(integer == null || integer % 2 != 0){
return false;
}
return true;
}
}
定义group
最后可以再定义两个接口作为group,代表两种不同的环境。
public interface Insert {
}
public interface Update {
}
修改实体
将我们定义好的注解,标注在level上,并设置groups为Insert时生效。
public class User {
private Long userId;
private String username;
private String tel;
@EvenNumber(groups = {
Insert.class})
private Integer level;
public User(Long userId, String username, String tel, Integer level) {
this.userId = userId;
this.username = username;
this.tel = tel;
this.level = level;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public Integer getLevel() {
return level;
}
public void setLevel(Integer level) {
this.level = level;
}
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", username='" + username + '\'' +
", tel='" + tel + '\'' +
", level=" + level +
'}';
}
}
修改Controller层
加上@Validated注解使实体的注解生效,并且设定在Insert的环境下才生效。如果这指定的是Update.class,则无法生效。
@RestController
public class UserController {
@PostMapping(value = "/register")
public String register(@Validated(value = {
Insert.class}) User user){
System.out.println(user);
return "success to register";
}
}
检验效果
可以看到在传入level为1时,注解会进行校验,返回错误的结果。后续可以对错误的异常进行进一步的完善。