0

I'm trying to add some extra validation logic on my REST beans using annotations. This is just an example, but the point is that the annotation is to be used on multiple REST resource objects / DTO's.

I was hoping for a solution like this:

public class Entity {

    @NotNull // JSR-303
    private String name;

    @Phone // Custom phonenumber that has to exist in a database
    private String phoneNumber;
}

@Component
public class PhoneNumberValidator implements Validator { // Spring Validator

    @Autowired
    private PhoneRepository repository;

    public boolean supports(Class<?> clazz) {
       return true;
    }

    public void validate(Object target, Errors errors) {
        Phone annotation = // find fields with annotations by iterating over target.getClass().getFields().getAnnotation
        Object fieldValue = // how do i do this? I can easily get the annotation, but now I wish to do a call to repository checking if the field value exists.
    }
}
LG87
  • 695
  • 1
  • 10
  • 20

2 Answers2

0

Did you try JSR 303 bean validator implementations like hibernate validator e.g. is available here http://www.codejava.net/frameworks/spring/spring-mvc-form-validation-example-with-bean-validation-api

Gibu George
  • 13
  • 1
  • 3
  • Yeah, as far as I've understood, I can't use Spring managed beans when using the JSR-303 way. E.g. inject a repository into the validator – LG87 May 28 '16 at 14:56
  • You can, the annotations are to be added on the DTO which you want to validate, have a look at the example. – Gibu George May 28 '16 at 15:00
  • The example does not provide custom annotations. As far as I can see, it only uses JSR-303 annotations to do validation. But if you want to create extra annotations, you can do that, but then you will create a JSR-303 validator, not a Spring validator. Example: http://goldenpackagebyanuj.blogspot.no/2013/05/custom-jsr-303-validation-using-constraintValidator.html But how would I go about, if I wish to inject a Repository into that custom ConstraintValidator? – LG87 May 28 '16 at 15:09
  • Just found this: http://stackoverflow.com/questions/21338883/how-to-autowire-service-in-constraintvalidator but still not exactly what I'm looking for, since the maven modules are separated. E.g: The annotations are located in a very small package that is shared among other projects, but using the @Constraint(validatedBy=) I have to include a lot more into that package, event Spring context and repository. – LG87 May 28 '16 at 15:12
  • Another way to put it, I wish to create validation annotations, that does not have the @ Constraint pointing to an implementation. The reason is exposed maven resources that other projects also use. I guess one solution would be using @ Constraint, but then setting another module as provided in maven and only have it included serverside. – LG87 May 28 '16 at 15:47
0

Maven Module A:

public interface RestValidator<A extends Annotation, T> extends ConstraintValidator<A, T>
public interface PhoneValidator extends RestValidator<PhoneNumber, String>

@Target(FIELD)
@Retention(RUNTIME)
@Constraint(validatedBy = PhoneValidator.class) // This usually doesnt work since its a interface
public @interface PhoneNumber {
   // JSR-303 required fields (payload, message, group)
}


public class Person {

@PhoneNumber
    private String phoneNumber;
}

Maven Module B:

@Bean
LocalValidatorFactoryBean configurationPropertiesValidator(ApplicationContext context, AutowireCapableBeanFactory factory) {
    LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
    factoryBean.setConstraintValidatorFactory(factory(context, factory));
    return factoryBean;
}

private ConstraintValidatorFactory factory(final ApplicationContext context, final AutowireCapableBeanFactory factory) {
    return new ConstraintValidatorFactory() {
        @Override
        public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
            if (RestValidator.class.isAssignableFrom(key)) {
                return context.getBean(key);
            } else {
                return factory.createBean(key);
            }
        }

        @Override
        public void releaseInstance(ConstraintValidator<?, ?> instance) {
            if (!(instance instanceof RestValidator<?, ?>)) {
                factory.destroyBean(instance);
            }
        }
    };
}

@Bean
WebMvcConfigurerAdapter webMvcConfigurerAdapter(final LocalValidatorFactoryBean validatorFactoryBean) {
    return new WebMvcConfigurerAdapter() {  // Adds the validator to MVC
        @Override
        public Validator getValidator() {
            return validatorFactoryBean;
        }
    };
}

Then I have a @Component implementation of PhoneValidator that has a Scope = Prototype.

I hate this solution, and I think Spring SHOULD look up on Interface implementations by default, but I'm sure some people that are a lot smarter than me made the decision not to.

LG87
  • 695
  • 1
  • 10
  • 20