自定义注解校验--ConstraintValidator

文章首发于有间博客,欢迎大伙光临! 自定义注解校验–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时,注解会进行校验,返回错误的结果。后续可以对错误的异常进行进一步的完善。
在这里插入图片描述

  • 11
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
好的,以下是一个自定义注解校验身份证的代码示例: 首先,我们需要定义一个注解 `@IdNumber`,用于标注需要进行身份证校验的字段: ```java @Documented @Constraint(validatedBy = IdNumberValidator.class) @Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface IdNumber { String message() default "身份证号码不正确"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } ``` 其中,`@Constraint(validatedBy = IdNumberValidator.class)` 表示该注解需要使用 `IdNumberValidator` 进行校验。 接着,我们需要实现 `IdNumberValidator`: ```java public class IdNumberValidator implements ConstraintValidator<IdNumber, String> { @Override public void initialize(IdNumber constraintAnnotation) { } @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (StringUtils.isBlank(value)) { return true; } return isIdNumber(value); } /** * 身份证校验 * * @param idNumber 身份证号码 * @return 是否有效 */ private boolean isIdNumber(String idNumber) { String regex = "^\\d{15}$|^\\d{17}[0-9Xx]$"; if (!idNumber.matches(regex)) { return false; } String provinceCode = idNumber.substring(0, 2); if (!ProvinceCodeUtils.isValidProvinceCode(provinceCode)) { return false; } if (!isValidBirthDate(idNumber)) { return false; } return isValidCheckCode(idNumber); } /** * 校验生日日期 * * @param idNumber 身份证号码 * @return 是否有效 */ private boolean isValidBirthDate(String idNumber) { String birthDateStr; if (idNumber.length() == 15) { birthDateStr = "19" + idNumber.substring(6, 12); } else { birthDateStr = idNumber.substring(6, 14); } SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd"); format.setLenient(false); try { format.parse(birthDateStr); } catch (ParseException e) { return false; } return true; } /** * 校验校验码 * * @param idNumber 身份证号码 * @return 是否有效 */ private boolean isValidCheckCode(String idNumber) { if (idNumber.length() == 15) { return true; } char[] idCharArray = idNumber.toCharArray(); int[] idIntArray = new int[17]; for (int i = 0; i < 17; i++) { idIntArray[i] = Integer.parseInt(String.valueOf(idCharArray[i])); } int[] weightFactorArray = new int[]{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}; int sum = 0; for (int i = 0; i < 17; i++) { sum += idIntArray[i] * weightFactorArray[i]; } int checkCodeIndex = sum % 11; char[] checkCodeArray = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'}; char checkCode = checkCodeArray[checkCodeIndex]; return checkCode == idCharArray[17]; } } ``` 其中,`isValid` 方法用于进行身份证校验,`isValidBirthDate` 方法用于校验生日日期,`isValidCheckCode` 方法用于校验校验码。 最后,在需要进行身份证校验的字段上使用 `@IdNumber` 注解即可: ```java public class User { @IdNumber private String idNumber; // getter/setter 略 } ``` 这样,在使用 `@Valid` 注解校验实体时,如果 `idNumber` 字段不符合身份证规则,则会抛出 `ConstraintViolationException` 异常。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值