前言:
前端传入的时间需要小于当前时间,因此想到了通过自定义注解实现,就无需在代码中写校验逻辑了。
1. 创建LocalDateTimeRange注解
package com.luck.living.validation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
/**
* @author xhga
*/
@Target(value = {
ElementType.METHOD,
ElementType.FIELD,
ElementType.ANNOTATION_TYPE,
ElementType.CONSTRUCTOR,
ElementType.PARAMETER,
ElementType.TYPE_USE
})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
validatedBy = LocalDateTimeRangeValidator.class
)
public @interface LocalDateTimeRange {
String message() default "时间范围不正确";
String pattern() default "yyyy-MM-dd HH:mm:ss";
// 最小值
String min() default "";
// 最大值
String max() default "";
// 最小值取当前时间,即:参数需大于当前时间
boolean minUseCurrentDate() default false;
// 最大值取当前时间,即:参数需小于当前时间
boolean maxUseCurrentDate() default false;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
2. 创建校验类
package com.luck.living.validation;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* @author xhga
*/
public class LocalDateTimeRangeValidator implements ConstraintValidator<LocalDateTimeRange, LocalDateTime> {
private LocalDateTime minDate;
private LocalDateTime maxDate;
boolean minUseCurrentDate = false;
boolean maxUseCurrentDate = false;
private boolean parseFail = false;
@Override
public void initialize(LocalDateTimeRange constraintAnnotation) {
minUseCurrentDate = constraintAnnotation.minUseCurrentDate();
maxUseCurrentDate = constraintAnnotation.maxUseCurrentDate();
try {
if (!constraintAnnotation.min().isEmpty()) {
minDate = LocalDateTime.parse(constraintAnnotation.min(), DateTimeFormatter.ofPattern(constraintAnnotation.pattern()));
}
if (!constraintAnnotation.max().isEmpty()) {
maxDate = LocalDateTime.parse(constraintAnnotation.max(), DateTimeFormatter.ofPattern(constraintAnnotation.pattern()));
}
} catch (Exception e) {
parseFail = true;
}
}
@Override
public boolean isValid(LocalDateTime value, ConstraintValidatorContext context) {
if (minUseCurrentDate) {
minDate = LocalDateTime.now();
}
if (maxUseCurrentDate) {
maxDate = LocalDateTime.now();
}
if (parseFail) {
return false;
}
// 如果value为null,返回true,因为这里只校验日期是否在范围内,不校验是否为null
if (value == null) {
return true;
}
// 如果minDate和maxDate都为null,不进行校验,返回true
if (minDate == null && maxDate == null) {
return true;
}
// 如果minDate不为null,且value小于minDate,返回false
if (minDate != null && value.isBefore(minDate)) {
return false;
}
// 如果maxDate不为null,且value大于maxDate,返回false
return maxDate == null || !value.isAfter(maxDate);
}
}
3. 使用LocalDateTimeRange注解
// createTimeEnd需小于当前时间
@LocalDateTimeRange(maxUseCurrentDate = true)
private LocalDateTime createTimeEnd;
与其他注解一样@LocalDateTimeRange与@Valid 搭配使用
注意:
遇到一个坑,我之前把当前时间的赋值写在了initialize方法,由于initialize只会在初始化时执行,导致比较当前时间时,只有第一次校验是取得当前时间,后续都是取得第一次的时间。
因此需要将获取当前时间的逻辑放在isValid方法。
// 错误代码
@Override
public void initialize(LocalDateTimeRange constraintAnnotation) {
try {
if (constraintAnnotation.minUseCurrentDate()) {
minDate = LocalDateTime.now();
} else if (!constraintAnnotation.min().isEmpty()) {
minDate = LocalDateTime.parse(constraintAnnotation.min(), DateTimeFormatter.ofPattern(constraintAnnotation.pattern()));
}
if (constraintAnnotation.maxUseCurrentDate()) {
maxDate = LocalDateTime.now();
} else if (!constraintAnnotation.max().isEmpty()) {
maxDate = LocalDateTime.parse(constraintAnnotation.max(), DateTimeFormatter.ofPattern(constraintAnnotation.pattern()));
}
} catch (Exception e) {
parseFail = true;
}
}