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... :(