从已提供的限制中继承
最常见的,就是继承@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()进行检验。