我正在创建一个注释来验证开始/结束日期,我想确保它们不重叠。
注释:
@Documented
@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = { NonOverlappingDatesValidator.class })
public @interface NonOverlappingDates {
String start();
String end();
String message() default "OVERLAPPING_OF_DATES";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
验证者:
public class NonOverlappingDatesValidator implements ConstraintValidator<NonOverlappingDates, Object> {
private String start;
private String end;
/**
* Initialize.
*/
@Override
public void initialize(NonOverlappingDates nonOverlappingDates) {
start = nonOverlappingDates.start();
end = nonOverlappingDates.end();
}
/**
* Checks if is valid.
*/
@Override
public boolean isValid(Object instanceToValidate, ConstraintValidatorContext constraintValidatorContext) {
Class<?> classToValidate = instanceToValidate.getClass();
LocalDateTime startDate;
LocalDateTime endDate;
try {
startDate = getLocalDateTimeFromFieldName(classToValidate, instanceToValidate, start);
endDate = getLocalDateTimeFromFieldName(classToValidate, instanceToValidate, end);
} catch (ReflectionException e) {
throw new RuntimeException(e);
}
return DateTimeUtils.isBefore(startDate, endDate);
}
private LocalDateTime getLocalDateTimeFromFieldName(Class<?> classToValidate, Object instanceToValidate, String fieldName) throws ReflectionException {
Method getter = ReflectionUtils.getPublicGetterOfFieldName(classToValidate, fieldName);
Object value = ReflectionUtils.invokeMethod(instanceToValidate, getter);
if (value instanceof LocalDateTime localDateTime) {
return localDateTime;
} else {
throw new RuntimeException(fieldName+" is not a LocalDateTime");
}
}
}
待验证的DTO:
@Data
@NonOverlappingDates(start = "startDate", end = "endDate")
public class IdentityCardDTO {
@JsonProperty
private Long id;
@JsonProperty
private String name;
@JsonProperty
private String surname;
@JsonProperty
@JsonSerialize(using = LocalDateSerializer.class)
@JsonDeserialize(using = LocalDateDeserializer.class)
private LocalDate dateOfBirth;
@JsonProperty
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime startDate;
@JsonProperty
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime endDate;
}
我想知道是否可以直接传递我想要验证的字段的值,而不是传递字段名称然后使用反射来检索值。
我不确定直接传递字段的值意味着什么,但是您可以通过抽象需要验证的数据来实现这种效果。您需要一个带有方法的接口来获取所需的数据。比方说:
public interface DatesData {
LocalDateTime startDate();
LocalDateTime endDate();
}
这不是一个很好的名字,但用于示例目的还可以。然后启用您的实际验证器来验证此接口的实例,而不是像现在这样
Object
。这样你也可以简化很多:
public class NonOverlappingDatesValidator implements ConstraintValidator<NonOverlappingDates, DatesData> {
@Override
public boolean isValid(DatesData data, ConstraintValidatorContext context) {
return data.startDate().isBefore(data.endDate());
}
}
NonOverlappingDatesValidator
现在可以验证实现 DatesData
的 any类。
IdentityCardDTO
变为:
@Data
@NonOverlappingDates
public class IdentityCardDTO implements DatesData {
//the rest of the fields, getters, setters, etc.
@Override
public LocalDateTime startDate() {
return startDate;
}
@Override
public LocalDateTime endDate() {
return endDate;
}
}
这也简化了
NonOverlappingDates
,因为您不再需要 start()
和 end()
方法。