Daniel Rusev
Daniel Rusev

Reputation: 1331

Custom bean validation by an abstract class or interface

Let's consider the following example, I'm creating a custom bean validation constraint by means of a new annotation type:

@Target( { METHOD, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = MyAbstractOrInterfaceValidator.class)
@Documented
public @interface MyAnnotation {

    String message() default "{}";

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

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

    SomeClass value();

}

My question is: can MyBastractOrInterfaceValidator class be an abstract class or an interface? How can I control which implementation of that interface or abstract class is used to validate an element the annotation is placed on?

Upvotes: 1

Views: 4005

Answers (2)

Gunnar
Gunnar

Reputation: 18990

No, that's not possible. If you want to hide your validator implementations from the public constraint definitions, you have the following options:

  • Provide a constraint mapping XML file and bundle it with your distribution. Users of the constraint will have to add the file to their validation.xml, though.
  • If you are using a DI solution such as CDI or Spring, provide just a very slim validator implementation as part of your public API (possibly as an inner class of the annotation) and obtain the actual implementation via dependency injection
  • If you are on Hibernate Validator 5.2 (currently under development), constraint validators can be registered via a META-INF/services file, which would be exactly what you are after; You even can plug in a custom constraint definition contributor for implementing other lookup strategies

Upvotes: 2

Nick Volynkin
Nick Volynkin

Reputation: 15089

See http://docs.oracle.com/javaee/7/api/javax/validation/Constraint.html

First, note that validatedBy is a Class<? extends ConstraintValidator<?,?>>[], that's an array, not a single class.

Second, there's no reason to use an interface or abstract class, because that class needs to be instantiated.

But, if you want to change the validator's implementation in the runtime, try using this:

public class MyValidator implements ConstraintValidator<MyAnnotation, String> {
    //store the Class information in a static variable
    private static Class<? extends ConstraintValidator<MyAnnotation, String>> implementationClass = MyValidatorOne.class;

    //and change it by an accessor
    public static void setImplementationClass(Class<? extends ConstraintValidator<MyAnnotation, String>> implClass) {
        this.implementationClass = implClass;
    }

    //this delegate will do all the job
    private final ConstraintValidator<MyAnnotation, String> implementation;
    public MyValidator() {
        implementation = implementationClass.newInstance();
    }

    @Override
    void initialize(MyAnnotation constraintAnnotation) {
        implementation.initialize(constraintAnnotation);
    }

    @Override
    boolean isValid(T value, ConstraintValidatorContext context) {
        return implementation.isValid(value, context);
    }
}

somewhere in the code:

MyValidator.setImplementationClass(MyValidatorTwo.class);

But there's one problem. Most probably an instance of each validator is created once in the runtime for a single class - on the first validation call on object of that class. Implementation change will only take effect if done before that.

Other way is to store implementationClass values in external class, like java.util.Properties or a class which picks available implementations with some priority.

Upvotes: 0

Related Questions