Using Asynchronous Requests and Calls in Spring Boot

This article explains how to implement asynchronous request handling and asynchronous method invocation in Spring Boot, covering servlet‑based async, Callable, WebAsyncTask, DeferredResult, @Async usage, common pitfalls, proxy solutions, and the differences between async requests and async calls.

Architect's Tech Stack
Architect's Tech Stack
Architect's Tech Stack
Using Asynchronous Requests and Calls in Spring Boot

Spring Boot provides several ways to handle long‑running operations without blocking the servlet thread, thereby increasing server throughput.

1. Asynchronous request with Servlet API – By calling request.startAsync() and registering an AsyncListener, the container thread is released while the business logic runs in a separate thread. The response is written back once the async processing completes.

@RequestMapping(value = "/email/servletReq", method = GET)
public void servletReq(HttpServletRequest request, HttpServletResponse response) {
    AsyncContext asyncContext = request.startAsync();
    asyncContext.addListener(new AsyncListener() { ... });
    asyncContext.setTimeout(20000);
    asyncContext.start(new Runnable() { ... asyncContext.complete(); });
    System.out.println("主线程:" + Thread.currentThread().getName());
}

2. Asynchronous request with Callable – Returning a Callable<String> lets Spring execute the call in a thread pool. Timeout and thread‑pool configuration can be customized via WebMvcConfigurerAdapter.

@RequestMapping(value = "/email/callableReq", method = GET)
@ResponseBody
public Callable<String> callableReq() {
    System.out.println("外部线程:" + Thread.currentThread().getName());
    return new Callable<String>() {
        @Override
        public String call() throws Exception {
            Thread.sleep(10000);
            System.out.println("内部线程:" + 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();
    }
}

3. Asynchronous request with WebAsyncTask – Similar to Callable but allows explicit timeout handling via onTimeout.

@RequestMapping(value = "/email/webAsyncReq", method = GET)
@ResponseBody
public WebAsyncTask<String> webAsyncReq() {
    System.out.println("外部线程:" + Thread.currentThread().getName());
    Callable<String> result = () -> {
        System.out.println("内部线程开始:" + Thread.currentThread().getName());
        TimeUnit.SECONDS.sleep(4);
        return "success";
    };
    WebAsyncTask<String> wat = new WebAsyncTask<>(3000L, result);
    wat.onTimeout(() -> "超时");
    return wat;
}

4. Asynchronous request with DeferredResult – Suitable for complex business logic; the result can be set later from another thread, and timeout/completion callbacks are supported.

@RequestMapping(value = "/email/deferredResultReq", method = GET)
@ResponseBody
public DeferredResult<String> deferredResultReq() {
    DeferredResult<String> result = new DeferredResult<>(60 * 1000L);
    result.onTimeout(() -> result.setResult("超时了!"));
    result.onCompletion(() -> System.out.println("调用完成"));
    myThreadPoolTaskExecutor.execute(() -> {
        System.out.println("内部线程:" + Thread.currentThread().getName());
        result.setResult("DeferredResult!!");
    });
    return result;
}

5. Asynchronous method invocation with @Async – Enable with @EnableAsync on the application class. Methods annotated with @Async run in a separate thread pool. The article discusses common pitfalls such as calling async methods from the same class, static or private methods, and how to obtain the Spring proxy (e.g., via ApplicationContext.getBean() or AopContext.currentProxy()).

@Controller
@RequestMapping("/app")
public class EmailController {
    @Autowired
    private ApplicationContext applicationContext;

    @RequestMapping(value = "/email/asyncCall", method = GET)
    @ResponseBody
    public Map<String, Object> asyncCall() {
        EmailController proxy = applicationContext.getBean(EmailController.class);
        proxy.testAsyncTask();
        return Map.of("code", 200);
    }

    @Async
    public void testAsyncTask() throws InterruptedException {
        Thread.sleep(10000);
        System.out.println("异步任务执行完成!");
    }
}

The article also explains how to enable CGLIB proxy exposure with @EnableAspectJAutoProxy(exposeProxy = true) to allow self‑invocation of async methods.

Differences between async request and async call – Async requests keep the client waiting for a response and are used to relieve server load, while async calls return immediately and perform background work such as logging or message production.

Conclusion – The tutorial covers the main patterns for asynchronous processing in Spring Boot, highlights proxy‑related issues, and provides practical code snippets for each approach.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AsynchronousSpring BootServletAsync
Architect's Tech Stack
Written by

Architect's Tech Stack

Java backend, microservices, distributed systems, containerized programming, and more.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.