Come fare @Async in primavera

1. Panoramica

In questo articolo, esploreremo il supporto dell'esecuzione asincrona in Spring e l' annotazione @Async .

In poche parole: annotare un metodo di un bean con @Async lo farà eseguire in un thread separato, ovvero il chiamante non aspetterà il completamento del metodo chiamato.

Un aspetto interessante della primavera è che il supporto degli eventi nel framework ha anche il supporto per l'elaborazione asincrona se vuoi seguire quella strada.

2. Abilita supporto asincrono

Iniziamo abilitando l'elaborazione asincrona con la configurazione Java, semplicemente aggiungendo @EnableAsync a una classe di configurazione:

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

L'annotazione di abilitazione è sufficiente, ma come ci si aspetterebbe, ci sono anche alcune semplici opzioni per la configurazione:

  • annotation - per impostazione predefinita, @EnableAsync rileva l'annotazione @Async di Springe l'EJB 3.1 javax.ejb.Asynchronous ; questa opzione può essere utilizzata anche per rilevare altri tipi di annotazioni definiti dall'utente
  • modalità : indica il tipo di consiglio da utilizzare: basato su proxy JDK o tessitura AspectJ
  • proxyTargetClass - indica il tipo di proxy che dovrebbe essere usato - CGLIB o JDK; questo attributo ha effetto solo se la modalità è impostata su AdviceMode.PROXY
  • order - imposta l'ordine in cuideve essere applicato AsyncAnnotationBeanPostProcessor ; per impostazione predefinita, viene eseguito per ultimo, solo in modo che possa tenere conto di tutti i proxy esistenti

L'elaborazione asincrona può anche essere attivata mediante configurazione XML - utilizzando il compito spazio dei nomi:

3. L' annotazione @Async

Innanzitutto, esaminiamo le regole - @Async ha due limitazioni:

  • deve essere applicato solo a metodi pubblici
  • l'autoinvocazione, ovvero la chiamata al metodo asincrono dalla stessa classe, non funzionerà

I motivi sono semplici: il metodo deve essere pubblico in modo che possa essere inviato tramite proxy. E l'autoinvocazione non funziona perché ignora il proxy e chiama direttamente il metodo sottostante.

3.1. Metodi con tipo restituito Void

Di seguito è riportato il modo semplice per configurare un metodo con tipo restituito void per essere eseguito in modo asincrono:

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

3.2. Metodi con tipo restituito

@Async può anche essere applicato a un metodo con tipo di ritorno, includendo il ritorno effettivo nel futuro:

@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 fornisce anche una classe AsyncResult che implementa Future . Può essere utilizzato per tenere traccia del risultato dell'esecuzione del metodo asincrono.

Ora, richiamiamo il metodo precedente e recuperiamo il risultato del processo asincrono utilizzando l' oggetto 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'esecutore

Per impostazione predefinita, Spring utilizza un SimpleAsyncTaskExecutor per eseguire effettivamente questi metodi in modo asincrono. Le impostazioni predefinite possono essere sovrascritte a due livelli: a livello di applicazione oa livello di singolo metodo.

4.1. Sostituisci l'executor a livello di metodo

L'esecutore richiesto deve essere dichiarato in una classe di configurazione:

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

Quindi il nome dell'esecutore dovrebbe essere fornito come attributo in @Async :

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

4.2. Sostituisci l'executor a livello di applicazione

La classe di configurazione dovrebbe implementare l' interfaccia AsyncConfigurer , il che significa che ha l'implementazione del metodo getAsyncExecutor () . È qui che restituiremo l'esecutore per l'intera applicazione: questo ora diventa l'esecutore predefinito per eseguire metodi annotati con @Async :

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

5. Gestione delle eccezioni

Quando un tipo restituito da un metodo è Future , la gestione delle eccezioni è facile: il metodo Future.get () genererà l'eccezione.

Tuttavia, se il tipo restituito è void , le eccezioni non verranno propagate al thread chiamante . Quindi dobbiamo aggiungere ulteriori configurazioni per gestire le eccezioni.

Creeremo un gestore di eccezioni asincrono personalizzato implementando l' interfaccia AsyncUncaughtExceptionHandler . Il metodo handleUncaughtException () viene richiamato quando sono presenti eccezioni asincrone non rilevate:

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); } } }

Nella sezione precedente, abbiamo esaminato l' interfaccia AsyncConfigurer implementata dalla classe di configurazione. Come parte di ciò, dobbiamo anche sovrascrivere il metodo getAsyncUncaughtExceptionHandler () per restituire il nostro gestore di eccezioni asincrono personalizzato:

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

6. Conclusione

In questo tutorial, abbiamo esaminato l' esecuzione di codice asincrono con Spring . Abbiamo iniziato con la configurazione e l'annotazione di base per farlo funzionare, ma abbiamo anche esaminato configurazioni più avanzate come fornire il nostro esecutore o strategie di gestione delle eccezioni.

E, come sempre, il codice completo presentato in questo articolo è disponibile su Github.