Spring Boot async example 2019-02-01 22:54
Generally we call methods in a synchronous manner. In some cases we need to call methods asynchronously. @Async
can help us call methods asynchronously when using Spring Boot. Before using @Async
we need to add an @EnableAsync
on the startup class. Here's a simple example that shows how to use @Async
to implement asynchronous method calls.
@Controller
@EnableAsync
@EnableAutoConfiguration
@ComponentScan(basePackages = "com.henryxi.async")
public class SimpleController {
@Autowired
private MyService myService;
@RequestMapping(value = "/hello", method = RequestMethod.GET)
@ResponseBody
public String hello(HttpServletRequest request) {
myService.hello();
return "finish";
}
public static void main(String[] args) {
SpringApplication.run(SimpleController.class, args);
}
}
We inject MyService
in controller. In this service there is an asynchronous method with @Async
annotation.
@Service
public class MyService {
@Async
public void hello() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("finish!!");
}
}
In this asynchronous method we let the program sleep for 3 seconds. So we can more clearly see how the asynchronous method works.
Start this app and visit this URL(localhost:8080/hello). The requested page is returned immediately. After 3 seconds, the log in the asynchronous method is output in the console in the background.
For a method that adds an async annotation, it is not called synchronously when it is called. In the spring framework there is a specific thread pool responsible for calling asynchronous methods and tasks. We can create ExecutorConfig
class to defined thread pool size.
@Configuration
@EnableAsync
public class ExecutorConfig {
private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);
@Bean(name = "threadPoolExecutor")
public Executor asyncServiceExecutor() {
logger.info("start asyncServiceExecutor");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(99999);
executor.setThreadNamePrefix("async-service-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
In order to use this thread pool we need to change @Asyc
to @Async("threadPoolExecutor")
. Rerun this app and access the URL. The output is like following.
......
2019-02-02 11:53:03.685 INFO 1488 --- [ main] com.henryxi.async.ExecutorConfig : start asyncServiceExecutor
2019-02-02 11:53:03.687 INFO 1488 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService
2019-02-02 11:53:03.691 INFO 1488 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'threadPoolExecutor'
2019-02-02 11:53:03.796 INFO 1488 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@67f639d3: startup date [Sat Feb 02 11:53:02 CST 2019]; root of context hierarchy
2019-02-02 11:53:03.837 INFO 1488 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/hello],methods=[GET]}" onto public java.lang.String com.henryxi.async.SimpleController.hello(javax.servlet.http.HttpServletRequest)
2019-02-02 11:53:03.839 INFO 1488 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2019-02-02 11:53:03.839 INFO 1488 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2019-02-02 11:53:03.866 INFO 1488 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2019-02-02 11:53:03.866 INFO 1488 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2019-02-02 11:53:03.894 INFO 1488 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2019-02-02 11:53:04.014 INFO 1488 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2019-02-02 11:53:04.060 INFO 1488 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2019-02-02 11:53:04.063 INFO 1488 --- [ main] com.henryxi.async.SimpleController : Started SimpleController in 1.928 seconds (JVM running for 2.502)
2019-02-02 11:53:05.382 INFO 1488 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2019-02-02 11:53:05.383 INFO 1488 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2019-02-02 11:53:05.394 INFO 1488 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 11 ms
async-service-1:finish!!
EOF