I have the same problem as here and here but couldn't find a solution yet.
So my sample test project will show the whole relevant configuration and code:
Constraint annotation:
@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = FooValidator.class)
public @interface FooValid {
String message();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Annotated PoJo:
public class Foo {
@FooValid(message = "Test failed")
private Integer test;
[...]
}
Annotated Service with @Validated:
@Service
@Validated
public class FooService {
private final Test test;
@Autowired
public FooService(final Test test) {
this.test = test;
}
public void foo(@Valid final Foo foo) {
this.test.test(foo);
}
}
JSR-303 ConstraintValidator:
public class FooValidator implements ConstraintValidator<FooValid, Integer> {
@Autowired
private ValidationService validationService;
@Override
public void initialize(final FooValid constraintAnnotation) {
// TODO Auto-generated method stub
}
@Override
public boolean isValid(final Integer value, final ConstraintValidatorContext context) {
// this.validationService is always NULL!
Assert.notNull(this.validationService, "the validationService must not be null");
return false;
}
}
Injected ValidationService:
@Service
public class ValidationService {
public void test(final Foo foo) {
System.out.println(foo);
}
}
Spring boot application and configuration:
@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application {
public static void main(final String[] args) {
final ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
final FooService service = context.getBean(FooService.class);
service.foo(new Foo());
}
@Bean
public static LocalValidatorFactoryBean validatorFactory() {
return new LocalValidatorFactoryBean();
}
@Bean
public static MethodValidationPostProcessor validationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
relevant maven pom:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.1.9.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<start-class>demo.Application</start-class>
<java.version>1.7</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
I'm using the LocalValidatorFactoryBean with the default SpringConstraintValidatorFactory. But why the dependency injection is not working in the ConstraintValidator and the ValidationService could not be autowired?
By the way if I don't use @Validated at the service, inject in opposite the spring or javax Validator interface and call manually "validator.validate" the dependency injection will work. But I don't want to call the validate method in every service manually.
Many thanks for help :)
Spring MVC Framework supports JSR-303 specs by default and all we need is to add JSR-303 and it's implementation dependencies in Spring MVC application.
Spring features a Validator interface that you can use to validate objects. The Validator interface works using an Errors object so that while validating, validators can report validation failures to the Errors object.
Hibernate Validator allows to express and validate application constraints. The default metadata source are annotations, with the ability to override and extend through the use of XML. It is not tied to a specific application tier or programming model and is available for both server and client application programming.
Validating constraints In the setUp() method, a Validator instance is retrieved from the ValidatorFactory . Validator instances are thread-safe and may be reused multiple times. The validate() method returns a set of ConstraintViolation instances, which you can iterate in order to see which validation errors occurred.
I have fought the same problem in Spring Boot environment and I found out that Hibernate internal implementation got in instead of the configured Spring's one. When the application started, debugger caught a line with the Spring's factory but later in runtime there was Hibernate's one. After some debugging, I came to the conclusion that MethodValidationPostProcessor got the internal one. Therefore I configured it as follows:
@Bean
public Validator validator() {
return new LocalValidatorFactoryBean();
}
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor(Validator validator) {
MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
methodValidationPostProcessor.setValidator(validator);
return methodValidationPostProcessor;
}
Note the setter for validator - it did the job.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With