自定义 SpringBoot Bean 验证

问题描述 投票:0回答:1

我正在创建一个注释来验证开始/结束日期,我想确保它们不重叠。

注释:

@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;
    
}

我想知道是否可以直接传递我想要验证的字段的值,而不是传递字段名称然后使用反射来检索值。

java spring-boot bean-validation
1个回答
2
投票

我不确定直接传递字段的值意味着什么,但是您可以通过抽象需要验证的数据来实现这种效果。您需要一个带有方法的接口来获取所需的数据。比方说:

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()
方法。

© www.soinside.com 2019 - 2024. All rights reserved.