原创

自定义注解校验--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时,注解会进行校验,返回错误的结果。后续可以对错误的异常进行进一步的完善。

在这里插入图片描述

java

  • 作者:LinJy(联系作者)
  • 发表时间:2020-10-28 10:33
  • 版权声明:自由转载-非商用-非衍生-保持署名(null)
  • undefined
  • 评论

    留言