In this tutorial, we’ll create an asynchronous REST controller in a Spring Boot application. By executing long-running tasks in background threads, the main thread can continue to handle other requests without being blocked. This can lead to a more responsive and scalable application.
Prerequisites
Make sure you have the following installed:
- Java Development Kit (JDK)
- Maven or Gradle
- IDE (such as IntelliJ IDEA, Eclipse, or Spring Tool Suite)
Step 1: Set Up a Spring Boot Project
Create a new Spring Boot project using your preferred build tool (Maven or Gradle). Ensure you have the necessary dependencies for Spring Web and Logging.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
Step 2: Enable and Configure Async in your Application
Firstly, let’s enable Async processing and configure the Executors for it:
@Configuration @EnableAsync public class AsyncConfig { @Bean(name="asyncExecutor") public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); executor.setMaxPoolSize(100); executor.setQueueCapacity(50); executor.setKeepAliveSeconds(60); executor.setThreadNamePrefix("async-"); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.setWaitForTasksToCompleteOnShutdown(true); executor.initialize(); return executor; } }
Here are the key points of the above Class:
- @EnableAsync: This annotation enables asynchronous processing in the application. It tells Spring Boot to scan the application for methods annotated with
@Async
and execute them in a separate thread pool. - @Bean(name=”asyncExecutor”): This annotation marks the
taskExecutor()
method as a Spring bean. It also specifies the name of the bean, which in this case is “asyncExecutor”. - ThreadPoolTaskExecutor taskExecutor(): This method defines a
ThreadPoolTaskExecutor
bean. This bean represents a thread pool that is responsible for executing asynchronous tasks.
Step 3: Configure a Long-running Service
Then, we will configure a Service Class which performs a long-running execution. We want to demonstrate that our REST Controller will return the execution immediately, while the long-running execution runs in a separate Thread:
@Service public class MyService { private static final Logger logger = LoggerFactory.getLogger(MyService.class); @Async("asyncExecutor") public void process() { // Simulate access to an external system that produces an ID UUID randomUUID = UUID.randomUUID(); // Convert UUID to a string String id = randomUUID.toString(); logger.info("Started processing request id "+id); try { Thread.sleep(15 * 1000); logger.info("Finished processing request id: "+id); } catch (InterruptedException ie) { logger.error("Error in ProcessServiceImpl.process(): {}", ie.getMessage()); } } }
Step 4: Add the Spring Boot Controller
Finally, we will add a Spring Boot Controller which maps our Asynchronous Service. There’s nothing really special about it:
@RestController public class AsyncController { @Autowired private MyService processService; @RequestMapping(value = "async", method = RequestMethod.GET) public ResponseEntity<Map<String, String>> async() { processService.process(); Map<String, String> response = new HashMap<>(); response.put("message", "Started processing your request"); return new ResponseEntity<>(response, HttpStatus.OK); } }
Step 5: Testing the Async REST Controller
Then, by testing the REST Controller, we can verify that requests to the /async endpoint will immediately return to the caller. Also, from the logs we can check that the Thread which is running the async Task belongs to the Async Executor Pool defined in our Bean:
By adding a SpringBootTest Class, we can also assert that the test execution completes before the Thread.sleep completes:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class AsyncControllerTest { @Autowired private TestRestTemplate restTemplate; @Test public void shouldReturnResponseInLessThen1Second() { long startTime = System.currentTimeMillis(); ResponseEntity<String> response = restTemplate. getForEntity("/async", String.class); Assertions.assertEquals(response.getStatusCode(), HttpStatus.OK); long endTime = System.currentTimeMillis(); assertThat(endTime - startTime).isLessThan(1000); } }
Conclusion
Congratulations! You’ve successfully created a Spring Boot application with an asynchronous REST controller that executes time-consuming tasks without blocking the main thread.
Source code: https://github.com/fmarchioni/masterspringboot/tree/master/rest/demo-rest-async
Found the article helpful? if so please follow us on Socials