美文网首页
Validation(3)自定义验证限制

Validation(3)自定义验证限制

作者: 张文超ai | 来源:发表于2019-06-07 12:24 被阅读0次

从已提供的限制中继承
最常见的,就是继承@Pattern,对正则表达式进行具体描述。例如一个数据库的描述为mysql://<name>:<password>@host/<databaseName>,我们需要能验证这个URL的正确。下面是自定义的标记@MysqlUrl

    /**
     * 用于检查一个mysql url,需要满足mysql://用户名:密码@host/数据库名字 
     * <p>
     * Accepts {@code CharSequence}. {@code null} elements are considered valid.
     * @author Wei 
     */
    /* @Target :可以在哪里使用,通常为METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE
     * 其中TYPE_USER需要Java 1.8*/
    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE,
        ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
    /* @Retention 必须是RUNTIME,否则Bean validation不会进行检验*/
    @Retention(RetentionPolicy.RUNTIME)
    /* 标注了这个标记的target,在javadoc中应显示其存在。在eclipse中似乎默认就是显示的 */
    @Documented 
    /* @Constraint是必须的,表明这是一个Bean Validation限制,否则会被忽略。
     * validatedBy中表明用来验证的constraintValidator的实现,本例继承@Pattern的限制,不需要任何的ConstraintValidator */
    @Constraint(validatedBy = {})
    /* @Pattern表明将继承@Pattern的限制,如果我们同时要求不为null,则还可以继承@NotNull,@NotBlank */
    @Pattern(regexp = "^mysql://[\\.a-zA-Z0-9`!#$%^&*'{}?/+=|_~-]+:[\\.a-zA-Z0-9`!#$%^&*'{}?/+=|_~-]+"
            + "@([a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)+(\\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*/[a-zA-Z0-9_]+$")
    /* @ReportAsSingleViolation表明作为一个限制,对于继承方式,这几乎是常规的。*/
    @ReportAsSingleViolation
    public @interface MysqlUrl {    
        //【part one】在学习限制时说过,有三个参数message,group,payload,这是标准参数,必须进行设置
        String message() default "{cn.wei.validation.mysqlurl.message}";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
        // 【part two】定义@MysqlUrl.List,也是标准方式,需进行设置    
        @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE,
            ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
        @Retention(RetentionPolicy.RUNTIME)
        @Documented    
        static @interface List {
            MysqlUrl[] value();
        }
    }

自定义验证器

@Ipv4小例子

以检查ipv4为例,当然我们也可以写@Pattern,我们也可以通过自定义的验证器来实现

    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE,
        ElementType.CONSTRUCTOR, ElementType.PARAMETER,ElementType.TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    //支持通过Ipv4Validator来进行验证
    @Constraint(validatedBy = {Ipv4Validator.class})
    //不允许为null,继承@NotNull
    @NotNull
    @ReportAsSingleViolation
    public @interface Ipv4 {
        //【part one】在学习限制时说过,有三个参数message,group,payload,这是标准参数,必须进行设置
        String message() default "{cn.wei.validation.ipv4.message}";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
        // 【part two】定义@Ipv4.List,也是标准方式,需进行设置    
        @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE,
            ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
        @Retention(RetentionPolicy.RUNTIME)
        @Documented    
        static @interface List {
            Ipv4[] value();
        }
    }


    public class Ipv4Validator implements ConstraintValidator<Ipv4, CharSequence>{
        @Override
        public void initialize(Ipv4 constraintAnnotation) {
        }
     
        @Override
        public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
            if(! (value instanceof String))
                return false;
            String str = (String) value;
            String[] p = str.split("\\.");
            if(p.length != 4)
                return false;
            for(int i = 0 ; i < 4 ; i ++){
                if(!StringUtils.isNumeric(p[i])
                        || Integer.parseInt(p[i],10) > 255
                        || (StringUtils.startsWith(p[i], "0") && ! StringUtils.equals(p[i], "0"))) //避免172.001.020.2的情况
                    return false;
            }
            return true;
        }
    }

@AdvanceFuture的例子
在@AdvanceFuture例子中,我们增加自定义标记中的参数 hours,表示在多少小时之后,以及显示的message消息中会根据hours来变化。

... ...
public @interface AdvanceFuture{
//我们应该使用{cn.wei.validation.AdvanceFuture.message},在properties文件中定义。
//作为演示例子,从简,直接写入内容:Not in the furture after {hours} hour(s),这里{hours}将填入hours参数,
//还可以使用{validatedValue}表示需要检查的值。更为复杂的如下 String message() default "Not in the furture{hours == 0 ? '' : hours == 1 ? ' after 1 hour' : ' after ' += hours += ' hours'}";
/**
* 增加一个新参数hours,类型为int,缺省值为0
* @return after n hours,default is 0(now)
*/
int hours() default 0;
... ...
}
}
public class AdvanceFutureValidator implements ConstraintValidator<AdvanceFuture, LocalDateTime>{
private int hour;

@Override
public void initialize(AdvanceFuture constraintAnnotation) {
    ConstraintValidator.super.initialize(constraintAnnotation);     
    this.hour = constraintAnnotation.hours();       
}
@Override
public boolean isValid(LocalDateTime value, ConstraintValidatorContext context) {
    if(value == null)
        return true;
    
    return value.compareTo(LocalDateTime.now().plusHours(hour)) > 0;
}

}

Constraint Validator的生命周期
对于自定义的限制:

先检查有没有继承其他限制,如果有,先进行这部分的检查
检查有没有定义的ConstraintValidator,如果没有,就完成检查,如果有多个,则找最匹配目标参数类型的那个。在上面的例子中,参数类型是CharSequence,如果还允许long,则会由另一个validator进行检验,这时:
@Constraint(validatedBy = {Ipv4StringValidator.class, Ipv4LongValidator.class})
找到Validator后,进行实例化,实例化先调用initialize()。对于同一个field的验证,这个实例会重复利用的。在initialize(),以@Size为例,可以获取注解中min和max的值。
然后调用isValid()进行检验。


作者:恺风
来源:CSDN
原文:https://blog.csdn.net/flowingflying/article/details/78150149
版权声明:本文为博主原创文章,转载请附上博文链接!

相关文章

网友评论

      本文标题:Validation(3)自定义验证限制

      本文链接:https://www.haomeiwen.com/subject/isumxctx.html