1. Información general
En este artículo, exploraremos el soporte de ejecución asincrónica en Spring y la anotación @Async .
En pocas palabras: anotar un método de un bean con @Async hará que se ejecute en un hilo separado, es decir, la persona que llama no esperará a que se complete el método llamado.
Un aspecto interesante en Spring es que el soporte de eventos en el marco también tiene soporte para el procesamiento asíncrono si desea seguir esa ruta.
2. Habilite la compatibilidad con Async
Comencemos habilitando el procesamiento asincrónico con la configuración de Java , simplemente agregando @EnableAsync a una clase de configuración:
@Configuration @EnableAsync public class SpringAsyncConfig { ... }
La anotación de habilitación es suficiente, pero como era de esperar, también hay algunas opciones simples para la configuración:
- anotación - por defecto, @EnableAsync detecta laanotación @Async de Springy el javax.ejb.Asynchronous de EJB 3.1; Esta opción también se puede utilizar para detectar otros tipos de anotaciones definidos por el usuario.
- modo - indica el tipo de consejo que se debe utilizar - JDK basado en proxy o tejido AspectJ
- proxyTargetClass : indica el tipo de proxy que se debe utilizar: CGLIB o JDK; este atributo tiene efecto solo si el modo se establece en AdviceMode.PROXY
- order : establece el orden en el que sedebe aplicar AsyncAnnotationBeanPostProcessor ; de forma predeterminada, se ejecuta en último lugar, solo para que pueda tener en cuenta todos los proxies existentes
El procesamiento asincrónico también se puede habilitar usando la configuración XML , usando el espacio de nombres de la tarea :
3. La anotación @Async
Primero, repasemos las reglas: @Async tiene dos limitaciones:
- debe aplicarse solo a métodos públicos
- autoinvocación: llamar al método asíncrono desde dentro de la misma clase, no funcionará
Las razones son simples: el método debe ser público para que pueda ser proxy. Y la autoinvocación no funciona porque omite el proxy y llama directamente al método subyacente.
3.1. Métodos con tipo de devolución nulo
A continuación se muestra la forma sencilla de configurar un método con un tipo de retorno vacío para que se ejecute de forma asincrónica:
@Async public void asyncMethodWithVoidReturnType() { System.out.println("Execute method asynchronously. " + Thread.currentThread().getName()); }
3.2. Métodos con tipo de retorno
@Async también se puede aplicar a un método con tipo de retorno, envolviendo el retorno real en el 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 también proporciona una clase AsyncResult que implementa Future . Esto se puede usar para rastrear el resultado de la ejecución del método asincrónico.
Ahora, invoquemos el método anterior y recuperemos el resultado del proceso asincrónico usando el objeto 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. El albacea
De forma predeterminada, Spring usa un SimpleAsyncTaskExecutor para ejecutar estos métodos de forma asincrónica. Los valores predeterminados se pueden anular en dos niveles: en el nivel de la aplicación o en el nivel del método individual.
4.1. Anular el ejecutor en el nivel de método
El ejecutor requerido debe declararse en una clase de configuración:
@Configuration @EnableAsync public class SpringAsyncConfig { @Bean(name = "threadPoolTaskExecutor") public Executor threadPoolTaskExecutor() { return new ThreadPoolTaskExecutor(); } }
Luego, el nombre del ejecutor debe proporcionarse como un atributo en @Async :
@Async("threadPoolTaskExecutor") public void asyncMethodWithConfiguredExecutor() { System.out.println("Execute method with configured executor - " + Thread.currentThread().getName()); }
4.2. Anular el ejecutor en el nivel de aplicación
La clase de configuración debe implementar la interfaz AsyncConfigurer , lo que significará que tiene implementado el método getAsyncExecutor () . Es aquí donde devolveremos el ejecutor para toda la aplicación; ahora se convierte en el ejecutor predeterminado para ejecutar métodos anotados con @Async :
@Configuration @EnableAsync public class SpringAsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { return new ThreadPoolTaskExecutor(); } }
5. Manejo de excepciones
Cuando un tipo de retorno de método es Future , el manejo de excepciones es fácil: el método Future.get () lanzará la excepción.
Pero, si el tipo de retorno es nulo , las excepciones no se propagarán al hilo de llamada . Por lo tanto, necesitamos agregar configuraciones adicionales para manejar excepciones.
Crearemos un controlador de excepción asíncrono personalizado implementando la interfaz AsyncUncaughtExceptionHandler . El método handleUncaughtException () se invoca cuando hay excepciones asincrónicas no detectadas:
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); } } }
En la sección anterior, analizamos la interfaz AsyncConfigurer implementada por la clase de configuración. Como parte de eso, también necesitamos anular el método getAsyncUncaughtExceptionHandler () para devolver nuestro controlador de excepciones asincrónico personalizado:
@Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new CustomAsyncExceptionHandler(); }
6. Conclusión
En este tutorial, analizamos la ejecución de código asincrónico con Spring . Comenzamos con la configuración y la anotación muy básicas para que funcione, pero también analizamos configuraciones más avanzadas, como proporcionar nuestro propio ejecutor o estrategias de manejo de excepciones.
Y, como siempre, el código completo presentado en este artículo está disponible en Github.