2014-12-17 6 views
7

Ich habe das gleiche Problem wie here und here aber konnte noch keine Lösung finden.Dependency Injection in JSR-303 Constraint Validator mit Spring fails

So wird meine Probe Testprojekt die gesamte relevante Konfiguration und Code zeigen:

Constraint Anmerkung:

@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 {}; 
} 

Kommentierte Pojo:

public class Foo { 

    @FooValid(message = "Test failed") 
    private Integer test; 
    [...] 
} 

Kommentierte Dienst mit @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; 
    } 

} 

injizierte ValidationService:

@Service 
public class ValidationService { 

    public void test(final Foo foo) { 
     System.out.println(foo); 
    } 
} 

Frühling Boot-Anwendung und Konfiguration:

@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> 

Ich bin mit dem LocalValidatorFactoryBean mit dem Standard SpringConstraintValidatorFactory. Aber warum funktioniert die Abhängigkeitsinjektion nicht im ConstraintValidator und der ValidationService konnte nicht autowired werden?

Durch die Art und Weise, wenn ich @Validated in den Dienst nicht verwenden, spritzen sich gegenüber dem Frühjahr oder javax Validator-Schnittstelle und manuell aufrufen „validator.validate“ die Dependency Injection funktioniert. Aber ich möchte die Validierungsmethode nicht in jedem Dienst manuell aufrufen.

Vielen Dank für die Hilfe :)

Antwort

0

Ich hatte das gleiche Problem. Das Problem tritt auf, weil Hibernate die Validatoren anstelle von Spring findet und anwendet. Sie müssen also Validierungsmodus NONE einzustellen, wenn die Konfiguration Hibernate:

@Bean(name="entityManagerFactory") 
public LocalContainerEntityManagerFactoryBean 
    localContainerEntityManagerFactoryBean(DataSource dataSource) { 
    LocalContainerEntityManagerFactoryBean lcemfb = 
     new LocalContainerEntityManagerFactoryBean(); 
    lcemfb.setDataSource(dataSource); 
    lcemfb.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); 
    lcemfb.setValidationMode(ValidationMode.NONE); 
    // Continue configuration... 

Wenn Sie ein LocalValidationFactoryBean Frühling alle Validatoren mit @Component kommentierte abholen konfguriert haben wird und autowire sie.

+0

Vielen Dank für Ihre Antwort! Nein, das geht nicht. In meinem obigen Beispielprojekt gibt es keinen Ruhezustand * LocalContainerEntityManagerFactoryBean *. Auch das Annotieren des Validators mit * @ Component * hilft nicht :(Haben Sie eine andere Idee? Das ist wirklich der ganze Code oben ... –

+1

Sie können die Hibernate-Validierung im Code mit einer benutzerdefinierten 'EntityManagerFactory' deaktivieren, wie ich es tat (siehe Anweisungen) für Spring Boot [hier] (http://docs.spring.io/spring-boot/docs/current/reference/html/howto-data-access.html)) oder Sie können die Hibernate-Validierung deaktivieren, indem Sie 'spring 'setzen. jpa.properties.javax.persistence.validation.mode = none "in' application.properties file' (siehe Anweisungen [hier] (http://stackoverflow.com/questions/26764532/how-to-disable-hibernate-validation- in-a-spring-boot-project)). –

7

Ich habe das gleiche Problem in Spring Boot-Umgebung bekämpft und ich fand heraus, dass interne Hibernate-Implementierung anstelle der konfigurierten Spring's einging. Als die Anwendung gestartet wurde, fing der Debugger eine Zeile mit der Spring-Factory ab, aber später in der Laufzeit gab es die von Hibernate. Nach einigem Debugging kam ich zu dem Schluss, dass MethodValidationPostProcessor den internen bekommen hat.Daher habe ich es wie folgt konfiguriert:

Beachten Sie die Setter für Validator - es hat die Aufgabe erledigt.

+0

Ich habe es gerade vor einer Minute getestet, und es funktioniert einfach mit 'return new MethodValidationPostProcessor()'. –

+0

Natürlich, warum habe ich nicht zuerst daran gedacht – davo

+0

Dies funktioniert wirklich, wenn Sie MethodValidationPostProcessor verwenden.Dies ist leider nicht der Fall in der Dokumentation, aber wenn Sie darüber nachdenken, macht es nur Sinn. Ich würde, hovewer, haben den Validator aus Spring-Kontext in die MethodeValidationPostProcessor injiziert, um die gleiche Instanz des Validators zu haben, anstatt die validator() -Methode aufzurufen. Ich werde Ihre Antwort bearbeiten. – martin