小架构step系列29:校验注解的组合

1 概述

如果遇到某些属性需要多种校验,比如需要非空、符合某正则表达式、长度不能超过某值等,如果这种属性只有有限几个,那么手工把对应的校验注解都加上即可。但如果这种属性比较多,那么重复加这些校验注解,也是一种代码重复。

hibernate-validator包提供了一种组合注解的方式,来解决上面场景的问题。

2 原理

2.1 例子

先来看一个组合校验注解的例子:

1) 定义一个新注解@FormatedString

import org.hibernate.validator.constraints.CompositionType;
import org.hibernate.validator.constraints.ConstraintComposition;

import javax.validation.Constraint;
import javax.validation.OverridesAttribute;
import javax.validation.Payload;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@ConstraintComposition(CompositionType.AND)
@NotEmpty
@Pattern(regexp = "")
@Size
@Constraint(validatedBy = {})
public @interface FormatedString {
    String message() default "{string.formated.message}";

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

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

    @OverridesAttribute(constraint = Size.class, name = "min")
    int min() default 0;

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

    @OverridesAttribute(constraint = Pattern.class, name = "regexp")
    String regexp() default "";
}

从上面看,@FormatedString注解的上方指定了非空(@NotEmpty)、正则表达式(@Pattern)、长度限制(@Size)这三个常用的的校验注解。@Constraint里面的validatedBy值为空,代表着自身没有提供ContraintValidator。@ConstraintComposition指定了多个注解校验结果的关系是AND,也就是都需要校验通过则总结果才算通过。

注意:使用@OverridesAttribute把参数传给原注解,这不属于Spring环境,不能使用Spring的@AliasFor来传参数。@Pattern的regexp属性没有给默认值,需要给一个默认值,否则无法通过编译。

2) 使用注解

// 以O开头且后面都是数字,长度在16-20之间
@FormatedString(regexp = "O[0-9]+", min = 16, max = 20)
private String orderNumber;

2.2 处理流程

详细的流程需要参考前面的文章,这里只给出涉及处理组合注解的部分。

// 源码位置:org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl
public ConstraintDescriptorImpl(ConstraintHelper constraintHelper,
        Constrainable constrainable,
        ConstraintAnnotationDescriptor<T> annotationDescriptor,
        ConstraintLocationKind constraintLocationKind,
        Class<?> implicitGroup,
        ConstraintOrigin definedOn,
        ConstraintType externalConstraintType) {
    this.annotationDescriptor = annotationDescriptor;
    this.constraintLocationKind = constraintLocationKind;
    this.definedOn = definedOn;
    this.isReportAsSingleInvalidConstraint = annotationDescriptor.getType().isAnnotationPresent(
            ReportAsSingleViolation.class
    );

    // 省略部分代码
    
    // 1. 当把校验注解的信息和属性封装为ConstraintDescriptorImpl时,还会解析一下校验注解里是否还带其它校验注解。
    //    annotationDescriptor为校验注解(如@NotNull),constrainable为属性字段信息(如标了@NotNull注解的mobilePhone属性字段),constraintType为GENERIC。
    //    返回结果赋值给了composingConstraints。
    this.composingConstraints = parseComposingConstraints( constraintHelper, constrainable, constraintType );
    this.compositionType = parseCompositionType( constraintHelper );
    validateCo
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值