Spring Boot Async Rest Controller: An example

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:

  1. @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.
  2. @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”.
  3. 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
Twitter Icon       Facebook Icon       LinkedIn Icon       Mastodon Icon