Spring permite la ejecución asíncrona de métodos de los beans de tal modo que estos métodos se ejecutan en su propio hilo y no bloquean al código que realiza la llamada hasta que finalice la ejecución del método invocado, el cual debe estar en un bean distinto.
Para ejecutar un método en un hilo propio basta con utilizar la anotación @Async disponible desde Spring 3.0
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class AsyncService { private static final Logger logger = LoggerFactory.getLogger(AsyncService.class); @Async public void execute(String msg) throws InterruptedException { logger.info("executed: " + msg); } }
También es imprescindible activar la ejecución de los métodos @Async utilizando la anotación @EnableAsync en una clase de configuración.
@EnableAsync @Configuration public class DemoApp {
La equivalencia en XML es
<task:annotation-driven />
No es necesario hacer nada más y ahora los métodos @Async se ejecutarán en su propio hilo cuando se invoquen desde otro bean, en nuestro ejemplo cualquiera que no sea AsyncService.
La ejecución es realizada dentro de un Runnable a través de una implementación de TaskExecutor. Spring crea una de forma automática pero generalmente vamos a querer definir y configurar un TaskExecutor según las necesidades de nuestro código. Lo que haremos es instanciar un ThreadPoolTaskExecutor y configurarlo si fuera necesario siguiendo la convención de denominarlo taskExecutor.
@EnableAsync @Configuration public class DemoApp { @Bean public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(2); executor.setMaxPoolSize(10); executor.setQueueCapacity(20); executor.initialize(); return executor; }
El anterior bean configura un pool de hilos con un tamaño comprendido entre 2 y 10, y una cola de peticiones de tamaño 20. Es recomendable definir estos parámetros de configuración fuera del código siguiendo el tutorial Ficheros de propiedades en Spring. La configuración equivalente en XML es la siguiente
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <property name="corePoolSize" value="2"/> <property name="maxPoolSize" value="10"/> <property name="queueCapacity" value="20"/> </bean>
Se pueden definir varios taskExecutor si fuera necesario y decidir cual utilizar en la propia anotación @Async
@Bean public Executor taskExecutor1() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(1); executor.setMaxPoolSize(5); executor.initialize(); return executor; }
@Async("taskExecutor1") public void execute(String msg) throws InterruptedException { logger.info("executed: " + msg); }
Otros tutoriales relacionados con Spring
Introducción a Spring Boot: Aplicación Web con servicios REST y Spring Data JPA
Spring Boot: Gestión de errores en aplicaciones web y REST
Testing en Spring Boot con JUnit 45. Mockito, MockMvc, REST Assured, bases de datos embebidas
Spring JDBC Template: simplificando el uso de SQL