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);
}
}
返回参数如下
使用postman测试校验
/**
*测试类
* @author ****
*/
@Slf4j
@RestController
@RequestMapping("/demo")
public class TestValidationController {
/**
* 测试校验手机号
* @param userInfo
*/
@PostMapping("/test/phone")
public void TestValidationPhone (@Valid @RequestBody UserInfo userInfo){
return;
}
}
postman校验返回请求参数和返回参数
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
- 文章目录
- 繁