Spring Asynchronous Request Processing via DeferredResult Class

Asynchronous via DeferredResult

I am going to discuss here how to perform asynchronous request processing via Spring’s DeferredResult class. A DeferredResult can be used when the application wants to produce the return value from a thread of its own choice. A DeferredResult is what is possibly not-yet-finished computation that will be available in future. Spring MVC 3.2 introduced a Servlet 3 based asynchronous request processing.

Normally once request processing is finished, you leave the controller handler method, but it is not the same case with DeferredResult. Spring MVC will hold on with the response, keeping the HTTP connection idle but opened. Later separate thread will resolve DeferredResult by assigning some value to it. Spring MVC will immediately pick up this event and send response to the browser, finishing the request processing.

Related Posts:

The Spring MVC controller method can also return the Callable similar to the DeferredResult instead of returning, as usual, a value and produce the return value from a separate thread.

Callable is an interface that is part of java.util package, and it is an improvement for the Runnable interface, because Callable allows to return a value, while Runnable does not.

DeferredResult is a class designed by Spring to allow more options for asynchronous request processing in Spring MVC, and this class just holds the result while your Callable implementation holds the async code, which is executed concurrently on behalf of the application.

DeferredResult has built-in callback methods such as onError(), onTimeout(), and onCompletion(), which makes error handling easier. In addition, as DeferredResult is just the result container, you can choose any thread (or thread pool) to run on your asynchronous code. With Callable, you do not have such choice.

For Callable, the return value is produced from a separate thread. Meanwhile the main Servlet container thread is released and allowed to process other requests. Spring MVC invokes the Callable in a separate thread with the help of a TaskExecutor and when the Callable returns, the request is dispatched back to the Servlet container to resume processing with the value returned by the Callable.

In this case of DeferredResult, the return value will also be produced from a separate thread. However, that thread is not known to Spring MVC. For example the result may be produced in response to some external event such as a JMS message, a scheduled task, etc.

Using @Async annotation, it becomes much more simple – annotating a method of a bean with @Async will make it execute in a separate thread. By default Spring uses a SimpleAsyncTaskExecutor to run method asynchronously. You can also override the default executor class to process your request. This is useful for tasks that could be executed concurrently.

Prerequisites

Jdk 12/19, Spring Boot 2.3.2/3.1.4, Maven 3.8.3/3.8.5

Request Processing via DeferredResult

Let’s create an example to process request asynchronously via deferred result.

@RestController
public class RoytutsRestController {

	private final static Logger logger = Logger.getLogger(RoytutsRestController.class.getName());

	@GetMapping("/test")
	public DeferredResult<String> getTestRequest() {
		logger.info("Request processing started");

		final DeferredResult<String> deferredResult = new DeferredResult<>();

		setResultInOtherThread(deferredResult);

		logger.info("Request processing finished");

		return deferredResult;
	}

	private void setResultInOtherThread(DeferredResult<String> deferredResult) {
		new Thread(() -> {
			logger.info("Deferred task started");

			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			logger.info("Deferred task finished");

			deferredResult.setResult("Test deferred result");
		}).start();
	}

}

In the above example I am resolving DeferredResult into a separate thread by setting a value to it. Once it is finished Spring MVC picks up the event and sends the response – Test deferred result – to the browser.

Spring Boot Main Class

A class with main method and @SpringBootApplication will deploy the application into embedded Tomcat container.

@SpringBootApplication
public class SpringDeferredResultAsyncRequestProcessApp {

	public static void main(String[] args) {
		SpringApplication.run(SpringDeferredResultAsyncRequestProcessApp.class, args);
	}

}

DeferredResult Callbacks

You can also use callback method on DeferredResult to set your results based on certain situations such as, on completion, on timeout or on error.

For example,

deferredResult.onCompletion(new Runnable() {
	@Override
	public void run() {
		deferredResult.setResult("Test deferred result");
	}
});

deferredResult.onTimeout(new Runnable() {
	@Override
	public void run() {
		deferredResult.setResult("Timeout");
	}
});

deferredResult.onError(new Consumer<Throwable>() {
	@Override
	public void accept(Throwable t) {
		deferredResult.setErrorResult(t);
	}
});

Testing the Async Processing Application

If you hit the URL http://localhost:8080/test in the browser you will see the following output in the browser.

spring asynchronous request processing via deferredresult class

Console Output

c.r.s.d.a.r.p.r.c.RoytutsRestController  : Request processing started
c.r.s.d.a.r.p.r.c.RoytutsRestController  : Request processing finished
c.r.s.d.a.r.p.r.c.RoytutsRestController  : Deferred task started
c.r.s.d.a.r.p.r.c.RoytutsRestController  : Deferred task finished

DeferredResult is useful in a situation when you want to use an entirely decoupled code for processing, e.g. putting some information in a queue which is being polled by some other thread.

Source Code

Download

Leave a Reply

Your email address will not be published. Required fields are marked *