가능한 한 간단하게 유지하려고 노력했습니다.
인터페이스 :
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = OneOfValidator.class)
@Documented
public @interface OneOf {
String message() default "{one.of.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String[] value();
}
검증 구현 :
public class OneOfValidator implements ConstraintValidator<OneOf, Object> {
private String[] fields;
@Override
public void initialize(OneOf annotation) {
this.fields = annotation.value();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(value);
int matches = countNumberOfMatches(wrapper);
if (matches > 1) {
setValidationErrorMessage(context, "one.of.too.many.matches.message");
return false;
} else if (matches == 0) {
setValidationErrorMessage(context, "one.of.no.matches.message");
return false;
}
return true;
}
private int countNumberOfMatches(BeanWrapper wrapper) {
int matches = 0;
for (String field : fields) {
Object value = wrapper.getPropertyValue(field);
boolean isPresent = detectOptionalValue(value);
if (value != null && isPresent) {
matches++;
}
}
return matches;
}
private boolean detectOptionalValue(Object value) {
if (value instanceof Optional) {
return ((Optional) value).isPresent();
}
return true;
}
private void setValidationErrorMessage(ConstraintValidatorContext context, String template) {
context.disableDefaultConstraintViolation();
context
.buildConstraintViolationWithTemplate("{" + template + "}")
.addConstraintViolation();
}
}
용법:
@OneOf({"stateType", "modeType"})
public class OneOfValidatorTestClass {
private StateType stateType;
private ModeType modeType;
}
메시지 :
one.of.too.many.matches.message=Only one of the following fields can be specified: {value}
one.of.no.matches.message=Exactly one of the following fields must be specified: {value}
Object
). 이 경우 값을 가져 오기 위해 리플렉션을 사용할 필요조차 없지만이 경우 유효성 검사기는 덜 일반적이됩니다. 2)BeanWrapperImp
Spring Framework (또는 다른 라이브러리) 및 해당getPropertyValue()
메서드에서 사용합니다. 이 경우 값을 가져와Object
필요한 모든 유형으로 캐스트 할 수 있습니다.