版本

来自springboot 版本 1.5.14.RELEASE

其中validation部分的版本号为:
<!-- 接口 -->
<dependency>
	<groupId>javax.validation</groupId>
	<artifactId>validation-api</artifactId>
	<version>1.1.0.Final</version>
</dependency>
<!-- 实现 -->
<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-validator</artifactId>
	<version>5.3.6.Final</version>
</dependency>

1.基于已有注解扩展

这里自定义一个既能判断非空,又判断有值string注解
/**
 * 这种扩展方法是基于已有的标准注解
 * 因此无需指定@Constraint(validatedBy = { })
 * 而加上该注解的作用是,让验证器实现将该注解作为一个标准约束来解析,而不是作为普通注解给忽略掉
 *
 * 注解解析实现将约束作为一个树结构来解析,
 * 比如约束注解A上面有约束注解B与C,约束注解C上面又有约束D与E
 * 那么形成的树为:
 * ConstraintTree
 *       A
 *       |
 *   --------
 *   |      |
 *   B      C
 *       ------
 *       |    |
 *       D    E
 * 这样从A开始递归,最终将每一个节点的约束都解析并收集违反约束的情况。
 * @ReportAsSingleViolation注解的作用是:
 * 因为每一个标准约束都会有自己的message可以输出,如果不使用@ReportAsSingleViolation的话
 * 会产生多个每个违反约束的信息输出,而使用了该注解可以只输出A上面定义的message
 *
 * 其中message、groups、payload 为必须属性
 *
 */
@Documented
@Constraint(validatedBy = { })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@NotNull
@Length
public @interface HasLength {

    /**
     * 关于message的语法
     * {}这种为hibernate validator中支持的参数化表达式
     * 其中可选参数为当前注解中的所有以方法名为key的参数
     * 比如这里可以定义信息为:
     * "字符串的长度不能小于{min}。"
     * 最终会转换为(根据min实际值替换):
     * "字符串的长度不能小于1。"
     * @return
     */
    String message() default "{com.example.constraints.HasLength}";

    /**
     * groups 为分组验证,不配置为Default组,
     * 这样可以根据不同的应用场景来定义一些group
     * 比如在执行save与update操作时,可能很多参数在update时就不是必须的了,
     * 这样就可以定义两个接口,分别表示Save.class组与Update.class组,
     * 在save操作时,使用@valid(group={Save.class}),这样就可以只对在Save.class标记的注解中进行约束验证
     * @return
     */
    Class<?>[] groups() default { };

    /**
     * payload这个参数
     * 应用并不多,可以通过它 来携带给验证器一些元数据信息
     * 比如自定义验证器时,验证对象可以是String、也可以是Optional<String>
     * 这时仅仅只用@NotNull 就无法正确验证了,这时候可以通过payload来标记一些需要特殊处理的操作
     * @return
     */
    Class<? extends Payload>[] payload() default { };

    /**
     * @OverridesAttribute注解表示所有该注解下的组合注解都共享这一个参数
     * 类似于spring中的@AliasFor
     * @return
     */
    @OverridesAttribute(constraint = Length.class, name = "min") int min() default 1;

    @OverridesAttribute(constraint = Length.class, name = "max") int max() default Integer.MAX_VALUE;
}

2.基于@Constraint扩展

这里定义一个判断目标时间是否在当前时间指定毫秒内的注解。

注解类

@Documented
@Constraint(validatedBy = {IntervalMillisValidatorForDate.class, IntervalMillisValidatorForLongTimestamp.class})
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
public @interface IntervalMillis {

    long millis() default 0;

    String message() default "{com.example.constraints.IntervalMillis}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

}

Constraint实现

public abstract class AbstractIntervalMillisValidator<T> implements ConstraintValidator<IntervalMillis, T> {
    protected long millis = -1;
    @Override
    public void initialize(IntervalMillis constraintAnnotation) {

        this.millis = constraintAnnotation.millis();

    }
    @Override
    public boolean isValid(T value, ConstraintValidatorContext context) {
        if (value != null) {
            return Math.abs(getCurrentInterval(value)) <= millis;
        }
        return true;
    }
    protected abstract long getCurrentInterval(T value);
}
public class IntervalMillisValidatorForDate extends AbstractIntervalMillisValidator<Date> {

    @Override
    protected long getCurrentInterval(Date value) {
        return System.currentTimeMillis() - value.getTime();
    }
}
public class IntervalMillisValidatorForLongTimestamp extends AbstractIntervalMillisValidator<Long> {

    @Override
    protected long getCurrentInterval(Long value) {
        return System.currentTimeMillis() - value;
    }
}
通过这两种方式,基本就可以实现定制自己想要的参数验证方式了。