4 Stimmen

Frühling: @Validated und @Async funktionieren nicht zusammen.

Ich versuche, @Validated und @Async in Kombination zu verwenden.

Bearbeitet: Ich möchte ein Eingabeobjekt validieren, wenn eine Methode meines Dienstes aufgerufen wird. Aber... nichts passiert. Meine Service-Methode wird nie aufgerufen. Wenn ich die @Async-Annotation aus der Servicemethode entferne, funktioniert die Validierung einwandfrei und der Methodenrumpf wird ausgeführt es wird wie erwartet eine ValidationContraintException ausgelöst.

Die (Teil-)Konfiguration:

@Configuration
@ComponentScan
@EnableAsync
public class MyConfiguration {

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }   
}

Mein Service:

@Service
@Validated
public class MyService {

    @Async
    public void doSomethingAsync(@NotNull @Valid final MyBean myBean) {
        // ...
    }

}

Ein Komponent, das den Service verwendet:

@Component
public class MyComponent {

    @Autowired
    protected MyService myService;

    public void doSomething(final MyBean myBean) {
        this.myService.doSomethingAsync(myBean); // returned without error
    }
}

Wo ist mein Fehler? Was kann ich tun, um es zum Laufen zu bringen?

Bearbeitung #1

Zuerst habe ich vergessen zu erwähnen, dass ich meine Methode mit einem ungültigen Bean aufrufe, um einen Validierungsfehler zu erzwingen.

Nach einigem Debuggen habe ich herausgefunden, dass der AnnotationAsyncExecutionInterceptor vor dem MethodValidationInterceptor ausgeführt wird. Dies erklärt das Verhalten. Wenn ich meine Methode mit einem ungültigen Bean aufrufe, wird die Bean-Validierung im Executor-Thread ausgeführt. Die ValidationConstraintException erreicht nie meinen Fehlerhandler im Hauptthread und der Aufruf der Methode myService.doSomethingAsync(myBean) wird ohne Fehler beendet.

Aber ich bin verwirrt darüber. Die apidocs von @EnableAsync beschreiben, dass der AsyncAnnotationBeanPostProcessor nach allen anderen Post-Prozessoren ausgeführt wird:

Gibt die Reihenfolge an, in der der {@link org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor} angewendet werden soll. Standardmäßig ist {@link Ordered#LOWEST_PRECEDENCE} in der Reihenfolge, um nach allen anderen Postprozessoren zu laufen, damit er einem bestehenden Proxy ein Advisor hinzufügen kann, anstatt einen doppelten Proxy zu erstellen.

Also würde ich erwarten, dass der AnnotationAsyncExecutionInterceptor nach allen anderen Interzeptoren für meine Methode ausgeführt wird.

Bearbeitung #2

Die Abhängigkeit cglib:cglib:3.1 ist bereits als Projekt-Abhängigkeit hinzugefügt und @EnableAsync(proxyTargetClass = true) scheint keine Auswirkung zu haben.

Bearbeitung #3

Ich habe mit dem order-Attribut von @EnableAsync und dem MethodValidationPostProcessor herumgespielt.

Wenn ich dem MethodValidationPostProcessor eine Reihenfolge von Ordered.HIGHEST_PRECEDENCE gebe, kann mein Service-Bean nicht für die Autobverdrahtung gefunden werden (aus irgendeinem Grund), Ordered.HIGHEST_PRECEDENCE + 1 funktioniert.

@Configuration
@ComponentScan
@EnableAsync
public class MyConfiguration {

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        final MethodValidationPostProcessor processor = new MethodValidationPostProcessor();

        processor.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
        return processor;
    }   
}

Mit einem Debugger kann ich sehen, dass die Reihenfolge der Postprozessoren wie erwartet ist. Der MethodValidationPostProcessor ist jetzt vor dem AsyncAnnotationBeanPostProcessor geordnet. Aber das hat absolut keine Auswirkung auf die Reihenfolge der Methodeninterzeptoren meiner Methode doSomethingAsync.

Bearbeitung #4

Vorerst habe ich alle @Async-Annotationen aus meinem Code entfernt. Bis jetzt habe ich noch keine Lösung. Das Problem besteht weiterhin... :(

0voto

Stephane Nicoll Punkte 31467

Beide Interzeptoren haben standardmäßig denselben order-Wert, sodass die Reihenfolge, in der sie aufgerufen werden, nicht deterministisch ist. In Ihrem Fall wird die asynchrone Annotation zuerst verarbeitet.

Sie können dies leicht beheben, indem Sie eine explizite Reihenfolge für Ihre Validierungsmethode angeben, basierend auf anderen Interzeptoren, die Sie möglicherweise in Ihren Services haben, zum Beispiel so

@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
    MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
    processor.setOrder(Ordered.LOWEST_PRECEDENCE - 1);
    return processor;
}   

Sie können auch eine Reihenfolge für @EnableAsync angeben, um explizit darauf hinzuweisen, dass Sie sich auf eine bestimmte Reihenfolge verlassen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X