Comment faire @Async au printemps

1. Vue d'ensemble

Dans cet article, nous explorerons la prise en charge de l'exécution asynchrone dans Spring - et l' annotation @Async .

En termes simples, annoter une méthode d'un bean avec @Async le fera s'exécuter dans un thread séparé, c'est-à-dire que l'appelant n'attendra pas la fin de la méthode appelée.

Un aspect intéressant de Spring est que la prise en charge des événements dans le framework prend également en charge le traitement asynchrone si vous souhaitez emprunter cette voie.

2. Activer la prise en charge Async

Commençons par activer le traitement asynchrone avec la configuration Java - en ajoutant simplement @EnableAsync à une classe de configuration:

@Configuration @EnableAsync public class SpringAsyncConfig { ... }

L'annotation d'activation est suffisante, mais comme vous vous en doutez, il existe également quelques options simples pour la configuration:

  • annotation - par défaut, @EnableAsync détecte l'annotation @Async de Springet l'EJB 3.1 javax.ejb.Asynchronous ; cette option peut également être utilisée pour détecter d'autres types d'annotations définis par l'utilisateur
  • mode - indique le type de conseil à utiliser - JDK proxy ou tissage AspectJ
  • proxyTargetClass - indique le type de proxy à utiliser - CGLIB ou JDK; cet attribut n'a d'effet que si le mode est défini sur AdviceMode.PROXY
  • order - définit l'ordre dans lequel AsyncAnnotationBeanPostProcessor doit être appliqué; par défaut, il s'exécute en dernier, juste pour pouvoir prendre en compte tous les proxys existants

Le traitement asynchrone peut également être activé à l'aide de la configuration XML - en utilisant l' espace de noms de la tâche :

3. L' annotation @Async

Tout d'abord - passons en revue les règles - @Async a deux limitations:

  • il doit être appliqué uniquement aux méthodes publiques
  • auto-invocation - appeler la méthode async depuis la même classe - ne fonctionnera pas

Les raisons sont simples: la méthode doit être publique pour pouvoir être mandatée. Et l' auto-invocation ne fonctionne pas car elle contourne le proxy et appelle directement la méthode sous-jacente.

3.1. Méthodes avec type de retour nul

Voici le moyen simple de configurer une méthode avec le type de retour void pour qu'elle s'exécute de manière asynchrone:

@Async public void asyncMethodWithVoidReturnType() { System.out.println("Execute method asynchronously. " + Thread.currentThread().getName()); }

3.2. Méthodes avec type de retour

@Async peut également être appliqué à une méthode avec un type de retour - en encapsulant le retour réel dans le futur:

@Async public Future asyncMethodWithReturnType() { System.out.println("Execute method asynchronously - " + Thread.currentThread().getName()); try { Thread.sleep(5000); return new AsyncResult("hello world !!!!"); } catch (InterruptedException e) { // } return null; }

Spring fournit également une classe AsyncResult qui implémente Future . Cela peut être utilisé pour suivre le résultat de l'exécution de la méthode asynchrone.

Maintenant, invoquons la méthode ci-dessus et récupérons le résultat du processus asynchrone à l'aide de l' objet Future .

public void testAsyncAnnotationForMethodsWithReturnType() throws InterruptedException, ExecutionException { System.out.println("Invoking an asynchronous method. " + Thread.currentThread().getName()); Future future = asyncAnnotationExample.asyncMethodWithReturnType(); while (true) { if (future.isDone()) { System.out.println("Result from asynchronous process - " + future.get()); break; } System.out.println("Continue doing something else. "); Thread.sleep(1000); } }

4. L'exécuteur

Par défaut, Spring utilise un SimpleAsyncTaskExecutor pour exécuter ces méthodes de manière asynchrone. Les valeurs par défaut peuvent être remplacées à deux niveaux - au niveau de l'application ou au niveau de la méthode individuelle.

4.1. Remplacer l'exécuteur au niveau de la méthode

L'exécuteur requis doit être déclaré dans une classe de configuration:

@Configuration @EnableAsync public class SpringAsyncConfig { @Bean(name = "threadPoolTaskExecutor") public Executor threadPoolTaskExecutor() { return new ThreadPoolTaskExecutor(); } }

Ensuite, le nom de l'exécuteur doit être fourni en tant qu'attribut dans @Async :

@Async("threadPoolTaskExecutor") public void asyncMethodWithConfiguredExecutor() { System.out.println("Execute method with configured executor - " + Thread.currentThread().getName()); }

4.2. Remplacer l'exécuteur au niveau de l'application

La classe de configuration doit implémenter l' interface AsyncConfigurer - ce qui signifie qu'elle a l'implémentation de la méthode getAsyncExecutor () . C'est ici que nous retournerons l'exécuteur pour l'ensemble de l'application - cela devient maintenant l'exécuteur par défaut pour exécuter les méthodes annotées avec @Async :

@Configuration @EnableAsync public class SpringAsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { return new ThreadPoolTaskExecutor(); } }

5. Gestion des exceptions

Lorsqu'un type de retour de méthode est un Future , la gestion des exceptions est facile - la méthode Future.get () lèvera l'exception.

Mais, si le type de retour est void , les exceptions ne seront pas propagées au thread appelant . Par conséquent, nous devons ajouter des configurations supplémentaires pour gérer les exceptions.

Nous allons créer un gestionnaire d'exceptions asynchrone personnalisé en implémentant l' interface AsyncUncaughtExceptionHandler . La méthode handleUncaughtException () est appelée en cas d'exceptions asynchrones non interceptées:

public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler { @Override public void handleUncaughtException( Throwable throwable, Method method, Object... obj) { System.out.println("Exception message - " + throwable.getMessage()); System.out.println("Method name - " + method.getName()); for (Object param : obj) { System.out.println("Parameter value - " + param); } } }

Dans la section précédente, nous avons examiné l' interface AsyncConfigurer implémentée par la classe de configuration. Dans ce cadre, nous devons également remplacer la méthode getAsyncUncaughtExceptionHandler () pour renvoyer notre gestionnaire d'exceptions asynchrone personnalisé:

@Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new CustomAsyncExceptionHandler(); }

6. Conclusion

Dans ce didacticiel, nous avons examiné l' exécution de code asynchrone avec Spring . Nous avons commencé avec la configuration et l'annotation très basiques pour le faire fonctionner, mais nous avons également examiné des configurations plus avancées telles que la fourniture de notre propre exécuteur ou des stratégies de gestion des exceptions.

Et, comme toujours, le code complet présenté dans cet article est disponible à l'adresse over sur Github.