JSR-303 bean validation with Spring does not kick in
bean-validation, hibernate-validator, spring
Solution
- A MethodValidationPostProcessor needs to be configured in addition to the LocalValidatorFactoryBean.
The class to be validated must have a `@Validated` annotation on it else methods are NOT searched for inline constraint annotations.
@Configuration
@ComponentScan(basePackageClasses = {SpringPackageComponentScanMarker.class})
@EnableAspectJAutoProxy
public abstract class AppConfig {
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
final MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
methodValidationPostProcessor.setValidator(validator());
return methodValidationPostProcessor;
}
@Bean
public LocalValidatorFactoryBean validator() {
final LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
return localValidatorFactoryBean;
}
}
...
@Service
@Validated
public class SpringBarista extends Barista {
The part of the reference manual that talks about integration with JSR-303 conveniently omits these 2 crucial points without which BV does not kick in. This just caused me 6 hours of debugging and hair tearing where I did everything the doc said but BV would simply not kick in. I finally had to debug through the Spring source code to understand this. There got to be an easier way and I can't be the only one who had this problem. Created a JIRA SPR-11473 for them to update the doc.
Problem
I've configured a JSR-303 custom validator following what's given in the docs (http://docs.spring.io/spring/docs/4.0.x/spring-framework-reference/html/validation.html), complete with LocalValidatorFactoryBean and Hibernate validator on the classpath. However, my validator just refuses to kick in. I've put up a dirt simple test project here (https://github.com/abhijitsarkar/java/tree/master/spring-jsr-303), along with a failing unit test. Should you decide to take a look, just clone it and run `gradlew clean test` from the root directory. I'm using Spring framework 4.0.2.RELEASE and Hibernate validator 5.0.3.Final. Method under validation: ``` public Coffee serve(@ValidOrder(Coffee.Blend.class) final String blend) { ``` ValidOrder annotation: ``` @Documented @Constraint(validatedBy = {OrderValidator.class}) @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER}) @NotNull public @interface ValidOrder { ``` OrderValidator validator: ``` public class OrderValidator implements ConstraintValidator<ValidOrder, String> { ``` Spring config: ``` @Configuration @ComponentScan(basePackages = "name.abhijitsarkar.coffeehouse") @EnableAspectJAutoProxy public abstract class AppConfig { @Bean public LocalValidatorFactoryBean validator() { return new LocalValidatorFactoryBean(); } } ``` Dependencies: ``` dependencies { compile( [group: 'javax.inject', name: 'javax.inject', version: injectApiVersion], [group: 'javax.validation', name: 'validation-api', version: beanValidationApiVersion], [group: 'javax.annotation', name: 'javax.annotation-api', version: annotationApiVersion], [group: 'org.springframework', name: 'spring-beans', version: springVersion], [group: 'org.springframework', name: 'spring-context', version: springVersion], [group: 'org.springframework', name: 'spring-aop', version: springVersion], [group: 'org.aspectj', name: 'aspectjrt', version: aspectjVersion] ) runtime( [group: 'org.hibernate', name: 'hibernate-validator', version: hibernateValidatorVersion], [group: 'javax.el', name: 'javax.el-api', version: elVersion], [group: 'org.glassfish.web', name: 'javax.el', version: glassfishElVersion], [group: 'org.aspectj', name: 'aspectjweaver', version: aspectjVersion] ) ```