Using Asynchronous Requests and Asynchronous Calls in Spring Boot
This article explains the concepts, implementation methods, and best practices for asynchronous requests and asynchronous method calls in Spring Boot, covering servlet‑based async, Callable, WebAsyncTask, DeferredResult, @EnableAsync/@Async usage, thread‑pool configuration, common pitfalls, and the differences between async requests and async calls.
1. Asynchronous Requests in Spring Boot
Async vs Sync : An asynchronous request releases the servlet thread and related resources, reducing server load; the response is delayed until the long‑running processing finishes, allowing higher request throughput.
In one sentence: it increases server throughput for client requests, though in production large concurrency is often handled by load balancers or message queues.
1.1 Implementation Methods
Four common ways to implement async requests:
Method 1 – Servlet Async
@RequestMapping(value = "/email/servletReq", method = GET)
public void servletReq(HttpServletRequest request, HttpServletResponse response) {
AsyncContext asyncContext = request.startAsync();
asyncContext.addListener(new AsyncListener() {
@Override public void onTimeout(AsyncEvent event) throws IOException { System.out.println("Timeout"); }
@Override public void onStartAsync(AsyncEvent event) throws IOException { System.out.println("Thread start"); }
@Override public void onError(AsyncEvent event) throws IOException { System.out.println("Error:" + event.getThrowable()); }
@Override public void onComplete(AsyncEvent event) throws IOException { System.out.println("Complete"); }
});
asyncContext.setTimeout(20000);
asyncContext.start(new Runnable() {
@Override public void run() {
try {
Thread.sleep(10000);
asyncContext.getResponse().setCharacterEncoding("utf-8");
asyncContext.getResponse().setContentType("text/html;charset=UTF-8");
asyncContext.getResponse().getWriter().println("This is an async response");
} catch (Exception e) { System.out.println("Exception:" + e); }
asyncContext.complete();
}
});
System.out.println("Main thread:" + Thread.currentThread().getName());
}Method 2 – Callable
@RequestMapping(value = "/email/callableReq", method = GET)
@ResponseBody
public Callable<String> callableReq() {
System.out.println("Outer thread:" + Thread.currentThread().getName());
return new Callable<String>() {
@Override public String call() throws Exception {
Thread.sleep(10000);
System.out.println("Inner thread:" + Thread.currentThread().getName());
return "callable!";
}
};
}
@Configuration
public class RequestAsyncPoolConfig extends WebMvcConfigurerAdapter {
@Resource private ThreadPoolTaskExecutor myThreadPoolTaskExecutor;
@Override public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {
configurer.setDefaultTimeout(60 * 1000);
configurer.setTaskExecutor(myThreadPoolTaskExecutor);
configurer.registerCallableInterceptors(timeoutCallableProcessingInterceptor());
}
@Bean public TimeoutCallableProcessingInterceptor timeoutCallableProcessingInterceptor() {
return new TimeoutCallableProcessingInterceptor();
}
}Method 3 – WebAsyncTask
@RequestMapping(value = "/email/webAsyncReq", method = GET)
@ResponseBody
public WebAsyncTask<String> webAsyncReq() {
System.out.println("Outer thread:" + Thread.currentThread().getName());
Callable<String> result = () -> {
System.out.println("Inner thread start:" + Thread.currentThread().getName());
Thread.sleep(4);
System.out.println("Inner thread return:" + Thread.currentThread().getName());
return "success";
};
WebAsyncTask<String> wat = new WebAsyncTask<>(3000L, result);
wat.onTimeout(() -> "Timeout");
return wat;
}Method 4 – DeferredResult
@RequestMapping(value = "/email/deferredResultReq", method = GET)
@ResponseBody
public DeferredResult<String> deferredResultReq() {
System.out.println("Outer thread:" + Thread.currentThread().getName());
DeferredResult<String> result = new DeferredResult<>(60 * 1000L);
result.onTimeout(() -> result.setResult("Timeout!"));
result.onCompletion(() -> System.out.println("Completed"));
myThreadPoolTaskExecutor.execute(() -> {
System.out.println("Inner thread:" + Thread.currentThread().getName());
result.setResult("DeferredResult!!");
});
return result;
}2. Asynchronous Calls in Spring Boot
Enable async method execution by adding @EnableAsync to the main class and annotating target methods with @Async (or @Async("threadPool") for a custom pool).
Common pitfalls:
Calling an @Async method from the same class bypasses the proxy, so the annotation has no effect.
Static or private methods cannot be async.
Without a custom TaskExecutor, Spring uses SimpleAsyncTaskExecutor, which creates a new thread for each task instead of reusing a pool.
Solutions include extracting async methods into a separate Spring‑managed bean, obtaining the proxy via ApplicationContext.getBean(...) or AopContext.currentProxy(), or enabling CGLIB proxy exposure with @EnableAspectJAutoProxy(exposeProxy = true).
@Controller
@RequestMapping("/app")
public class EmailController {
@Autowired private ApplicationContext applicationContext;
@RequestMapping(value = "/email/asyncCall", method = GET)
@ResponseBody
public Map<String, Object> asyncCall() {
Map<String, Object> res = new HashMap<>();
EmailController proxy = applicationContext.getBean(EmailController.class);
proxy.testAsyncTask();
res.put("code", 200);
return res;
}
@Async
public void testAsyncTask() throws InterruptedException {
Thread.sleep(10000);
System.out.println("Async task completed!");
}
}3. Difference Between Async Request and Async Call
Async requests are used to relieve server pressure by holding the servlet thread while the long‑running task executes, eventually returning a response to the client; they improve request throughput.
Async calls return immediately to the client, delegating the heavy work to background threads (e.g., logging to Kafka). The client does not wait for the background task to finish.
4. Summary
Both async requests and async calls are essential techniques for handling time‑consuming operations in Spring Boot. Proper configuration of thread pools, understanding proxy mechanics, and choosing the right async model are key to effective implementation.
Future articles will dive deeper into Spring AOP dynamic proxies and related advanced topics.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
