Spring MVCというか、JSR-303でメソッドを使用したヴァリデーション

メンバaの値が条件Aでメンバbの値が条件Bだったら、メンバcの値は条件Cの時のみ許可する、みたいな複雑な条件を行う時の話について。
メソッドで判定処理を書けば簡単に済む場合の、メソッドを使ったヴァリデーターの作成方法。

実装

アノテーションと実装はこんな感じのものを用意。

@Target({ METHOD, FIELD, TYPE, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Constraint(validatedBy = ValidationMethodValidator.class)
@Documented
public @interface ValidationMethod {

    String message() default "{smart.extension.spring.validator.ValidationMethod.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    String method();

    String target() default "";

    @Target({ METHOD, FIELD, TYPE, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
    @Retention(RUNTIME)
    @Documented
    @interface List {

        ValidationMethod[] value();
    }
}
public class ValidationMethodValidator implements ConstraintValidator<ValidationMethod, Object> {

    private String method;

    private String target;

    @Override
    public void initialize(final ValidationMethod constraintAnnotation) {
        method = constraintAnnotation.method();
        target = constraintAnnotation.target();
    }

    @Override
    public boolean isValid(final Object value, final ConstraintValidatorContext context) {
        boolean isValid = false;
        if (value != null) {
            try {
                Method m = value.getClass().getMethod(method);
                isValid = (Boolean)m.invoke(value);
            } catch (Exception e) {
                throw new WrapRuntimeException(e);
            }
        }

        if (!isValid) {
            context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()).addNode(target).addConstraintViolation()
                    .disableDefaultConstraintViolation();
        }

        return isValid;
    }
}

サンプル

typeが1か2だったらdataは空以外を、typeが1か2以外だったらdataは空を要求するような場合の記述はこんな感じで。

@ValidationMethod(method = "validateData", target = "data")
public static class Data {

    private int type;

    private String data;

...

    public boolean validateData() {
        if ((type == 1) || (type == 2)) {
            return !StringUtil.isEmpty(data);
        } else {
            return StringUtil.isEmpty(data);
        }
    }
}

targetは、前回のFieldCompareValidatorの話に同じでどのメンバのエラーにするかの設定だす(・ω・)