Spring Boot Asynchronous Controller example
As of Servlet 3, it is possible to handle a HTTP request asynchronously by releasing the thread that handles the HTTP request. This is useful when a call takes some time to send the response, so instead of blocking the thread, you can do the processing in the background and return the data when the processing is done by another thread.
Spring MVC supports following return types that are processed asynchronously : DeferredResult<V>, ListenableFuture<V>, CompletionStage<V>, CompletableFuture<V>, Callable<V>, ResponseBodyEmitter<V>, SeeEmitter<V>, StreamingResponseBody<V>.
Below example shows how to send the response to a HTTP request asynchronously in a Spring Boot MVC application.
Step 1) Add below dependency in pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
Step 2) Create AynchronousControllerApplication class
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AynchronousControllerApplication { public static void main(String[] args) { SpringApplication.run(AynchronousControllerApplication.class, args); } }
Step 3) Create AynchronousController class which will send reponse asynchronously using Callable
package com.example.demo; import java.util.concurrent.Callable; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class AynchronousController { @GetMapping() public Callable<String> hello() { return () -> { Thread.sleep(2000); // simulate time consuming call return "Hello Aynchronous Controller"; }; } }
Sending reponse asynchronously from the Controller using CompletableFuture
package com.example.demo; import java.util.concurrent.CompletableFuture; import org.springframework.core.task.TaskExecutor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class AynchronousController { private final TaskExecutor taskExecutor; public AynchronousController(TaskExecutor taskExecutor) { this.taskExecutor = taskExecutor; } @GetMapping() public CompletableFuture<String> hello() { return CompletableFuture.supplyAsync(() -> { delay(2000); // simulate time consuming call return "Hello Aynchronous Controller"; }, taskExecutor ); } private void delay(long time) { try { Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } } }
Step 4) Running AynchronousControllerApplication
Console Output :
2020-01-18 10:55:31.320 INFO 4352 --- [main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.21] 2020-01-18 10:55:31.648 INFO 4352 --- [main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2020-01-18 10:55:31.648 INFO 4352 --- [main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 8809 ms 2020-01-18 10:55:32.506 INFO 4352 --- [main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2020-01-18 10:55:33.131 INFO 4352 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2020-01-18 10:55:33.131 INFO 4352 --- [main] c.e.d.AynchronousControllerApplication : Started AynchronousControllerApplication in 12.524 seconds (JVM running for 13.742)
Step 5) Testing AynchronousControllerApplication
Open any browser and launch http://localhost:8080. You will see 'Hello Aynchronous Controller' message displayed in the broswer.
Note that in the logs below, a new thread (nio-8080-exec-1) is started in the backend to handle the request. Then another thread (task-1) did the actual processing and the reponse was submitted by another thread (nio-8080-exec-4) to the dispatcher servlet.
2021-09-03 11:39:03.992 DEBUG 9284 --- [nio-8080-exec-1] o.apache.coyote.http11.Http11Processor : Socket: Status in: [OPEN_READ], State out: [LONG] 2021-09-03 11:39:03.992 DEBUG 9284 --- [nio-8080-exec-1] o.apache.coyote.http11.Http11Processor : State after async post processing: [LONG] 2021-09-03 11:39:05.999 DEBUG 9284 --- [task-1] o.s.w.c.request.async.WebAsyncManager : Async result set, dispatch to / 2021-09-03 11:39:06.000 DEBUG 9284 --- [task-1] o.apache.catalina.core.AsyncContextImpl : Req: 503167fb CReq: 5ef6938d RP: 449c882f Stage: 7 Thread: task-1 State: N/A Method: dispatch URI: / 2021-09-03 11:39:06.219 DEBUG 9284 --- [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet : Completed 200 OK