Backend Development 11 min read

Using Asynchronous Requests and Calls in Spring Boot

This article explains how to implement asynchronous requests and asynchronous method calls in Spring Boot, covering servlet‑based async, Callable, WebAsyncTask, DeferredResult, @Async annotation, thread‑pool configuration, common pitfalls, and the differences between async request and async call.

Java Captain
Java Captain
Java Captain
Using Asynchronous Requests and Calls in Spring Boot

1. Asynchronous Request in Spring Boot

Asynchronous requests free the servlet thread while the long‑running processing continues in another thread, improving server throughput. Four implementation methods are demonstrated:

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("超时了...");
        }
        @Override public void onStartAsync(AsyncEvent event) throws IOException {
            System.out.println("线程开始");
        }
        @Override public void onError(AsyncEvent event) throws IOException {
            System.out.println("发生错误:" + event.getThrowable());
        }
        @Override public void onComplete(AsyncEvent event) throws IOException {
            System.out.println("执行完成");
        }
    });
    asyncContext.setTimeout(20000);
    asyncContext.start(new Runnable() {
        @Override public void run() {
            try {
                Thread.sleep(10000);
                System.out.println("内部线程:" + Thread.currentThread().getName());
                asyncContext.getResponse().setCharacterEncoding("utf-8");
                asyncContext.getResponse().setContentType("text/html;charset=UTF-8");
                asyncContext.getResponse().getWriter().println("这是异步的请求返回");
            } catch (Exception e) {
                System.out.println("异常:" + e);
            }
            asyncContext.complete();
        }
    });
    System.out.println("主线程:" + Thread.currentThread().getName());
}

Method 2: Callable

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

Method 3: WebAsyncTask with timeout callback

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

Method 4: DeferredResult for complex business logic

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

2. Asynchronous Method Calls in Spring Boot

Enable async processing by adding @EnableAsync to the main application class. Annotate methods with @Async("threadPool") where threadPool is a custom ThreadPoolTaskExecutor. The method must be public, non‑static, and invoked via a Spring‑managed proxy; direct self‑calls bypass the proxy and will not be asynchronous.

Common pitfalls include calling async methods within the same class, using static or private methods, and relying on the default SimpleAsyncTaskExecutor which creates a new thread for each task. Defining a reusable thread pool improves performance.

Example of obtaining the proxy manually:

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

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

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

When using CGLIB proxying, you can also retrieve the current proxy via AopContext.currentProxy() and invoke the async method on it.

3. Difference Between Async Request and Async Call

Async requests are used to relieve server pressure under high concurrency, keeping the client waiting for a response; async calls return immediately and perform background work (e.g., logging, Kafka publishing) without blocking the client.

4. Summary

Both async request and async call techniques are covered, with code examples and best‑practice tips. Understanding Spring’s dynamic proxy mechanism is essential for correctly applying @Async, and further exploration of AOP proxies will deepen mastery.

JavaBackend DevelopmentasynchronousSpring BootServletCallableDeferredResult
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

login 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.