J'ai un doute sur ce code:
@Async
public CompletableFuture<String> doFoo() {
CompletableFuture<String> fooFuture = new CompletableFuture<>();
try {
String fooResult = longOp();
fooFuture.complete(fooResult);
} catch (Exception e) {
fooFuture.completeExceptionally(e);
}
return fooFuture;
}
La question est: doFoo ne retourne-t-il fooFuture qu'après la fin de longOp (correctement ou exceptionnellement) et retourne donc des futurs déjà terminés ou Spring fait-il de la magie et revient-il avant d'exécuter le corps? Si le code bloque sur longOp (), comment exprimeriez-vous que le calcul est transmis à un exécuteur?
Peut-être cela? De toute autre manière?
@Async
public CompletableFuture<String> doFoo() {
CompletableFuture<String> completableFuture = new CompletableFuture<>();
CompletableFuture.runAsync(() -> {
try {
String fooResult = longOp();
completableFuture.complete(fooResult);
} catch (Exception e) {
completableFuture.completeExceptionally(e);
}
});
return completableFuture;
}
Spring fait en fait tout le travail derrière les couvertures, vous n'avez donc pas à créer le CompletableFuture
vous-même. Fondamentalement, l'ajout de @Async
l'annotation est comme si vous appeliez votre méthode d'origine (sans l'annotation) comme:
CompletableFuture<User> future = CompletableFuture.runAsync(() -> doFoo());
Quant à votre deuxième question, afin de la transmettre à un exécuteur, vous pouvez spécifier le nom du bean exécuteur dans le valeur du @Async
annotation, comme ceci:
@Async("myExecutor")
public CompletableFuture<User> findUser(String usernameString) throws InterruptedException {
User fooResult = longOp(usernameString);
return CompletableFuture.completedFuture(fooResult);
}
Ce qui précède serait fondamentalement le suivant comme si vous appeliez votre méthode d'origine, comme:
CompletableFuture<User> future = CompletableFuture.runAsync(() -> doFoo(), myExecutor);
Et toute votre logique exceptionally
que vous feriez avec le CompletableFuture
renvoyé de cette méthode.