上回我们说到spring boot的@validated注解结合@ControllerAdvice实现统一参数校验,末尾我们抛出一个问题,就是@Validated内置的校验注解并不能满足所有的情况,今天我们就来说说这种情况该怎么解决,我们总不能一边用注解一边还手写if、else吧,bi格一下就降下来了,我们写漂亮的代码,不就是为了让人看嘛。所以针对这个我们,我们就用自定义注解来解决。

一、自定义注解(以身份证校验举例)

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE ,ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER })
@Inherited
@Documented
@Constraint(validatedBy = IdCardValidator.class)
public @interface IDCARD {
    String message() default "身份证不正确,请核对后重新填写";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

@Retention :用来说明该注解类的生命周期。它有以下三个参数:
RetentionPolicy.SOURCE : 注解只保留在源文件中
RetentionPolicy.CLASS : 注解保留在class文件中,在加载到JVM虚拟机时丢弃
RetentionPolicy.RUNTIME : 注解保留在程序运行期间,此时可以通过反射获得定义在某个类上的所有注解。

@Target : 用来说明该注解可以被声明在那些元素之前。
ElementType.TYPE:说明该注解只能被声明在一个类前。
ElementType.FIELD:说明该注解只能被声明在一个类的字段前。
ElementType.METHOD:说明该注解只能被声明在一个类的方法前。
ElementType.PARAMETER:说明该注解只能被声明在一个方法参数前。
ElementType.CONSTRUCTOR:说明该注解只能声明在一个类的构造方法前。
ElementType.LOCAL_VARIABLE:说明该注解只能声明在一个局部变量前。
ElementType.ANNOTATION_TYPE:说明该注解只能声明在一个注解类型前。
ElementType.PACKAGE:说明该注解只能声明在一个包名前。

@Constraint来指定自定义注解的实现接口

二、编写实现类

public class IdCardValidator implements ConstraintValidator<IDCARD, String> {
    @Override
    public boolean isValid(String cardNo, ConstraintValidatorContext context) {
        if (StringUtils.isEmpty(cardNo)) {
            return true;
        }

        if (null != cardNo && cardNo.trim().length() == 15) {
            cardNo = cardNo.trim();
            StringBuffer sb = new StringBuffer(cardNo);
            sb.insert(6, "19");
            sb.append(transCardLastNo(sb.toString()));
            cardNo = sb.toString();
        }
        //18位身份证 正则表达
        try {
            IsValidldCardInterface isValidld = new IsValidIdCardImpl();
            String result = isValidld.isValidldCard(cardNo);
            if ("".equals(result)) {
                return true;
            }
        } catch (Exception e) {
            return false;
        }
        return false;
    }

    /**
     * @author ken chen
     * <p>Title: transCardLastNo</p>
     * <p>Description: 15 转换 18身份证补位运算 </p>
     */
    private Object transCardLastNo(String newCardId) {
        char[] ch = newCardId.toCharArray();
        int m = 0;
        int[] co = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
        char[] verCode = new char[]{'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};
        for (int i = 0; i < newCardId.length(); i++) {
            m += (ch[i] - '0') * co[i];
        }
        int residue = m % 11;
        return String.valueOf(verCode[residue]);
    }

    @Override
    public void initialize(IDCARD constraintAnnotation) {

    }

}

注意上面实现类集实现了ConstraintValidator<IDCARD, String>接口,IDCARD是自定义的注解,String是要简要的参数类型。

围绕着@Validated注解我们能讲的就讲完了,写代码不就是越简洁越好吗,其实基于注解我们能玩出好多花来,比如接口幂等,统一权限,统一日志等等,我们以后再来慢慢展开。