validator自定义校验注解及使用

2023年8月9日12:09:19

validator自定义校验注解及使用

官方文档:https://docs.jboss.org/hibernate/validator/8.0/reference/en-US/html_single/#validator-customconstraints

用到依赖:


<!--validator的依赖如果项目使用的springBoot的依赖可以不用再引入
hibernate-validator 因为SpringBoot中封装了validator直接使用就可以-->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>8.0.0.Alpha3</version>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.3.1</version>
</dependency>

可以分为四种使用方式:

1简单自定义注解约束:

校验手机号注解

/**
 * 检验手机号注解
 * @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE, TYPE_USE}):
 * 定义约束支持的目标元素类型。@CheckCase
 * 可用于字段(元素类型FIELD)、
 * JavaBeans
 *  属性以及方法返回值(METHOD)、
 *  方法/构造函数参数(PARAMETER)
 *  和参数化类型的类型参数(TYPE_USE)。
 *  元素类型ANNOTATION_TYPE允许基于
 *  .@CheckCase
 * 创建类级别约束时(参见第 2.1.4 节,“类级别约束”TYPE ),
 * 必须使用元素类型。针对构造函数返回值的约束需要支持元素类型CONSTRUCTOR。
 * 用于同时验证方法或构造函数的所有参数的交叉参数约束(参见 第 6.3 节,“交叉参数约束”METHOD )必须分别支持or CONSTRUCTOR。
 *
 * @Retention(RUNTIME):指定,这种类型的注解将在运行时通过反射的方式可用
 *
 * @Constraint(validatedBy = CheckCaseValidator.class): 将注解类型标记为约束注解,并指定用于验证用 注释的元素的验证器@CheckCase。
 * 如果一个约束可以用于多种数据类型,则可以指定多个验证器,每个数据类型一个。
 *
 * @Documented:说,使用@CheckCase将包含在用它注释的元素的JavaDoc中
 *
 * @Repeatable(List.class): 表示注解可以在同一个地方重复多次,通常使用不同的配置。List是包含注释类型。
 *
 * @author ***
 */
@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE, TYPE_USE })
@Retention(RUNTIME)
@Constraint(validatedBy = CheckPhoneValidator.class)
@Documented
@Repeatable(List.class)
public @interface CheckPhone {

    /**
     * 默认提示信息
     * @return
     */
    String message() default "默认的提示!!";

    /**
     *分组使用
     * @return
     */
    Class<?>[] groups() default { };

    /**
     * 在ValidatorFactory初始化期间定义约束验证器有效负载
     * @return
     */
    Class<? extends Payload>[] payload() default { };

    /**
     * 指定使用什么逻辑校验手机号
     * @return
     */
    PhoneModeEnum value();

    @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
    @Retention(RUNTIME)
    @Documented
    @interface List {
        CheckPhone[] value();
    }
}

调用校验手机号逻辑


/**
 * 自定义校验逻辑
 * @author ****
 */
public class CheckPhoneValidator implements ConstraintValidator<CheckPhone, String> {

    private PhoneModeEnum phoneMode;

    /**
     * initialize()方法使您可以访问已验证约束的属性值,并允许您将它们存储在验证器的字段中
     * @param constraintAnnotation
     */
    @Override
    public void initialize(CheckPhone constraintAnnotation) {
        this.phoneMode = constraintAnnotation.value();
    }

    /**
     * isValid()方法包含实际的验证逻辑
     * @param mobile
     * @param constraintContext
     * @return
     */
    @Override
    public boolean isValid(String mobile, ConstraintValidatorContext constraintContext) {
       if (ObjectUtil.isNull(mobile)) {
            return true;
        }
        if (ObjectUtil.equal(phoneMode ,PhoneModeEnum.IS_MOBILE_HK)) {
            return PhoneUtil.isMobileHk(mobile);
        } else if ((ObjectUtil.equal(phoneMode ,PhoneModeEnum.IS_MOBILE_TW))){
            return PhoneUtil.isMobileTw(mobile);
        } else if ((ObjectUtil.equal(phoneMode ,PhoneModeEnum.IS_MOBILE_MO))){
            return PhoneUtil.isMobileMo(mobile);
        } else if ((ObjectUtil.equal(phoneMode ,PhoneModeEnum.IS_MOBILE))){
            return PhoneUtil.isMobile(mobile);
        } else {
            return PhoneUtil.isPhone(mobile);
        }

    }
}

指定使用什么逻辑校验手机号

/**
 *指定校验逻辑使用
 * @author ***
 */

public enum PhoneModeEnum {
    /**
     * 香港手机号
     */
    IS_MOBILE_HK,
    /**
     * 台湾手机号
     */
    IS_MOBILE_TW,
    /**
     * 澳门手机号
     */
    IS_MOBILE_MO,
    /**
     * 大陆手机号
     */
    IS_MOBILE,
    /**
     * 中国手机号
     */
    IS_PHONE;
}

手机号校验逻辑



/**
 * 电话号码工具类,
 * 借鉴hutool工具类中的
 *
 * @author ***
 */
public class PhoneUtil {

    /**
     * 中国大陆移动电话
     * eg: 中国大陆: +86  180 4953 1399,2位区域码标示+13位数字
     * 中国大陆 +86 Mainland China
     */
    public final static Pattern MOBILE = Pattern.compile("(?:0|86|\\+86)?1[3456789]\\d{9}");

    /**
     * 中国香港移动电话
     * eg: 中国香港: +852 5100 4810, 三位区域码+10位数字, 中国香港手机号码8位数
     * eg: 中国大陆: +86  180 4953 1399,2位区域码标示+13位数字
     * 中国大陆 +86 Mainland China
     * 中国香港 +852 Hong Kong
     * 中国澳门 +853 Macao
     * 中国台湾 +886 Taiwan
     */
    public final static Pattern MOBILE_HK = Pattern.compile("(?:0|852|\\+852)?\\d{8}");

    /**
     * 中国台湾移动电话
     * eg: 中国台湾: +886 09 60 000000, 三位区域码+号码以数字09开头 + 8位数字, 中国台湾手机号码10位数
     * 中国台湾 +886 Taiwan 国际域名缩写:TW
     */
    public final static Pattern MOBILE_TW = Pattern.compile("(?:0|886|\\+886)?(?:|-)09\\d{8}");

    /**
     * 中国澳门移动电话
     * eg: 中国台湾: +853 68 00000, 三位区域码 +号码以数字6开头 + 7位数字, 中国台湾手机号码8位数
     * 中国澳门 +853 Macao 国际域名缩写:MO
     */
    public final static Pattern MOBILE_MO = Pattern.compile("(?:0|853|\\+853)?(?:|-)6\\d{7}");

    /**
     * 验证是否为中国大陆(大陆)
     *
     * @param value 值
     * @return 是否为手机号码(大陆)
     * @since 5.3.11
     */
    public static boolean isMobile(CharSequence value) {
        return isMatchRegex(MOBILE, value);
    }

    /**
     * 验证是否为手机号码(香港)
     *
     * @param value 手机号码
     * @return 是否为香港手机号码
     * @author dazer, ourslook
     * @since 5.6.3
     */
    public static boolean isMobileHk(CharSequence value) {
        return isMatchRegex(MOBILE_HK, value);
    }

    /**
     * 验证是否为手机号码(台湾)
     *
     * @param value 手机号码
     * @return 是否为台湾手机号码
     * @author ihao
     * @since 5.6.6
     */
    public static boolean isMobileTw(CharSequence value) {
        return isMatchRegex(MOBILE_TW, value);
    }

    /**
     * 验证是否为手机号码(澳门)
     *
     * @param value 手机号码
     * @return 是否为澳门手机号码
     * @author ihao
     * @since 5.6.6
     */
    public static boolean isMobileMo(CharSequence value) {
        return isMatchRegex(MOBILE_MO, value);
    }

    /**
     * 验证是否为手机号码(中国)
     *
     * @param value 值
     * @return 手机号码(大陆)+手机号码(香港)+手机号码(台湾)+手机号码(澳门)
     * @since 5.3.11
     */
    public static boolean isPhone(CharSequence value) {
        return isMobile(value) || isMobileHk(value) || isMobileTw(value) || isMobileMo(value);
    }


    /**
     * 通过正则表达式验证
     *
     * @param pattern 正则模式
     * @param value   值
     * @return 是否匹配正则
     */
    public static boolean isMatchRegex(Pattern pattern, CharSequence value) {
        return isMatch(pattern, value);
    }

    /**
     * 给定内容是否匹配正则
     *
     * @param pattern 模式
     * @param content 内容
     * @return 正则为null或者""则不检查,返回true,内容为null返回false
     */
    public static boolean isMatch(Pattern pattern, CharSequence content) {
        if (content == null || pattern == null) {
            // 提供null的字符串为不匹配
            return false;
        }
        return pattern.matcher(content).matches();
    }
}

使用案例

/**
 *用户信息
 * @author ***
 */
public class UserInfo  implements Serializable {
    /**
     * 使用注解指定这个字段需要校验
     * value 指定使用大陆校验方式
     * message 自定义提示信息 
     *  ${validatedValue} 代表返回信息携带请求参数 
     *  提示信息 : 123132 手机号格式异常
     */
    @CheckPhone(value = PhoneModeEnum.IS_PHONE,message = "${validatedValue}"+"手机号码格式异常!")
    private String mobile;

}
使用main方法测试校验

/** 
 * 测试自定义手机校验
 * @author ****
 */
@Slf4j
public class TestValidationPhone {
    public static void main(String[] args) {

        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
        UserInfo userInfo = new UserInfo();
        userInfo.setMobile("123123");
        Set<ConstraintViolation<UserInfo>> checkInfo = validator.validate(userInfo);
	    System.out.println(checkInfo);
    }

}

返回参数如下validator自定义校验注解及使用
使用postman测试校验
/**
 *测试类
 * @author ****
 */
@Slf4j
@RestController
@RequestMapping("/demo")
public class TestValidationController {
    /**
     * 测试校验手机号
     * @param userInfo
     */
    @PostMapping("/test/phone")
    public void TestValidationPhone (@Valid @RequestBody UserInfo userInfo){
        return;
    }

}

postman校验返回请求参数和返回参数validator自定义校验注解及使用

2简单自定义类注解约束:(作用到类上面的注解)

校验汽车超载的注解:
/**
 * 校验汽车类人数是否超载注解
 *
 * @Target(ElementType.TYPE)//接口、类、枚举、注解
 * @Target(ElementType.FIELD)//字段、枚举的常量
 * @Target(ElementType.METHOD)//方法
 * @Target(ElementType.PARAMETER)//方法参数
 * @Target(ElementType.CONSTRUCTOR) //构造函数
 * @Target(ElementType.LOCAL_VARIABLE)//局部变量
 * @Target(ElementType.ANNOTATION_TYPE)//注解
 * @Target(ElementType.PACKAGE)//包
 * 
 */
@Target({ TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = { ValidPassengerCountValidator.class })
@Documented
public @interface ValidPassengerCount {

    /**
     * 提示信息
     * @return
     */
    String message() default "默认提醒信息";

    /**
     * 分组使用
     * @return
     */
    Class<?>[] groups() default { };

    /**
     *在ValidatorFactory初始化期间定义约束验证器有效负载
     * @return
     */
    Class<? extends Payload>[] payload() default { };
}
校验逻辑:
/**
 * 校验汽车类人数是否超载逻辑
 */
public class ValidPassengerCountValidator implements ConstraintValidator<ValidPassengerCount, Car> {

    @Override
    public void initialize(ValidPassengerCount constraintAnnotation) {
    }

    @Override
    public boolean isValid(Car car, ConstraintValidatorContext context) {
        if (car == null) {
            return true;
        }

        return car.getPassengers().size() <= car.getSeatCount();
    }
}
乘客类
/**
 * 乘客类
 * @author ***
 */
public class Person {

   private String name;

   public Person(String name) {
      this.name = name;
   }

}
汽车类:
/**
 * 汽车类
 * @author ***
 */
@ValidPassengerCount(message = "乘客数不得超过座位数.")
public class Car {

    private int seatCount;

    private List<Person> passengers;

    public Car(int seatCount, List<Person> passengers) {
        this.seatCount = seatCount;
        this.passengers = passengers;
    }

    public int getSeatCount() {
        return seatCount;
    }

    public List<Person> getPassengers() {
        return passengers;
    }
}

main方法测试类:

/**
 * 测试自定义校验
 * @author ****
 */
@Slf4j
public class TestValidationCar {
    public static void main(String[] args) {

        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
        Person person1 = new Person("小明");
        Person person2 = new Person("小红");
        Person person3 = new Person("小李");
        Person person4 = new Person("小王");
        Person person5 = new Person("小爱");
        ArrayList<Person> people = new ArrayList<>();
        people.add(person1);
        people.add(person2);
        people.add(person3);
        people.add(person4);
        people.add(person5);
        Car car = new Car(4,people);
        Set<ConstraintViolation<Car>> validate = validator.validate(car);

        System.out.println(validate);

    }

}

3跨参数约束:

跨参数约束适用于方法的参数数组或构造函数,可用于表达依赖于多个参数值的验证逻辑。

校验两个时间字段是否第一个时间是在后一个时间之前的注解:

/**
 * 多参数校验只适用于
 * 方法上面
 * 构造函数
 * 注解
 *
 */
@Constraint(validatedBy = ConsistentDateParametersValidator.class)
@Target({ METHOD, CONSTRUCTOR, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Documented
public @interface ConsistentDateParameters {

    String message() default "多参数校验!!!";

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

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

/**
 *
 * @SupportedValidationTarget(ValidationTarget.PARAMETERS)表示这个类是多参数校验使用 
 * 多参数校验逻辑 
 * 
 */
@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class ConsistentDateParametersValidator implements ConstraintValidator<ConsistentDateParameters, Object[]> {

    @Override
    public void initialize(ConsistentDateParameters constraintAnnotation) {
    }

    @Override
    public boolean isValid(Object[] value, ConstraintValidatorContext context) {
        if (value.length != 2) {
            throw new IllegalArgumentException("Illegal method signature");
        }

        //leave null-checking to @NotNull on individual parameters
        if (value[0] == null || value[1] == null) {
            return true;
        }

        if (!(value[0] instanceof Date) || !(value[1] instanceof Date)) {
            throw new IllegalArgumentException(
                    "Illegal method signature, expected two " +
                            "parameters of type Date."
            );
        }

        return ((Date) value[0]).before((Date) value[1]);
    }
}
多参数校验main方法
/**
 * 测试自定义多参数校验
 * @author ****
 */
public class TestValidationParams {
    public static void main(String[] args) {

        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
     	//使用多参数校验的类
        ExecutableValidator executableValidator = factory.getValidator().forExecutables();

        CalendarService object = new CalendarService();
        Method method = null;
        try {
            method 

  • 作者:奇怪的逻辑
  • 原文链接:https://blog.csdn.net/weixin_47628154/article/details/125088181
    更新时间:2023年8月9日12:09:19 ,共 9459 字。