Mastering Asynchronous Execution in Spring Boot: An In‑Depth Guide

This article explains how to implement asynchronous processing in Spring Boot using @Async annotations, JDK 8 CompletableFuture, Callable, WebAsyncTask, DeferredResult, and AsyncHandlerInterceptor, providing detailed code examples and configuration tips to improve performance and scalability of backend services.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
Mastering Asynchronous Execution in Spring Boot: An In‑Depth Guide

Asynchronous Execution

There are two main ways to achieve asynchronous execution in Spring Boot:

Use the @Async annotation on methods and enable it with @EnableAsync on the configuration class.

Leverage JDK 8's CompletableFuture for fine‑grained control.

@AllArgsConstructor
public class AskThread implements Runnable {
    private CompletableFuture<Integer> re = null;
    public void run() {
        int myRe = 0;
        try {
            myRe = re.get() * re.get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(myRe);
    }
    public static void main(String[] args) throws InterruptedException {
        final CompletableFuture<Integer> future = new CompletableFuture<>();
        new Thread(new AskThread(future)).start();
        // Simulate a long‑running calculation
        Thread.sleep(1000);
        // Notify completion
        future.complete(60);
    }
}

In this example a thread is started; the AskThread instance blocks on myRe = re.get() * re.get() until the result is supplied, after which execution continues.

The CompletableFuture.supplyAsync method creates a CompletableFuture that runs the supplied function in a new thread, returning immediately while the computation proceeds in the background.

public class Calc {
    public static Integer calc(Integer para) {
        try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
        return para * para;
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        final CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> calc(50))
            .thenApply(i -> Integer.toString(i))
            .thenApply(str -> "\"" + str + "\"")
            .thenAccept(System.out::println);
        future.get();
    }
}

When supplyAsync is used without specifying a thread pool, it runs in the common ForkJoinPool whose threads are daemon threads; they terminate when the main thread ends.

Using Callable for Asynchronous Calls

@RestController
public class HelloController {
    private static final Logger logger = LoggerFactory.getLogger(HelloController.class);
    @Autowired private HelloService hello;
    @GetMapping("/helloworld")
    public String helloWorldController() { return hello.sayHello(); }
    /** Asynchronous call: Spring MVC will execute the Callable in a TaskExecutor */
    @GetMapping("/hello")
    public Callable<String> helloController() {
        logger.info(Thread.currentThread().getName() + " entering helloController");
        Callable<String> callable = new Callable<String>() {
            @Override public String call() throws Exception {
                logger.info(Thread.currentThread().getName() + " entering call");
                String say = hello.sayHello();
                logger.info(Thread.currentThread().getName() + " returned from helloService");
                return say;
            }
        };
        logger.info(Thread.currentThread().getName() + " returning from helloController");
        return callable;
    }
}

WebAsyncTask with Timeout Handling

@RestController
public class HelloController {
    private static final Logger logger = LoggerFactory.getLogger(HelloController.class);
    @Autowired private HelloService hello;
    @GetMapping("/world")
    public WebAsyncTask<String> worldController() {
        logger.info(Thread.currentThread().getName() + " entering helloController");
        WebAsyncTask<String> webAsyncTask = new WebAsyncTask<>(3000, new Callable<String>() {
            @Override public String call() throws Exception {
                logger.info(Thread.currentThread().getName() + " entering call");
                String say = hello.sayHello();
                logger.info(Thread.currentThread().getName() + " returned from helloService");
                return say;
            }
        });
        logger.info(Thread.currentThread().getName() + " returning from helloController");
        webAsyncTask.onCompletion(() -> logger.info(Thread.currentThread().getName() + " completed"));
        webAsyncTask.onTimeout(() -> { logger.info(Thread.currentThread().getName() + " onTimeout"); throw new TimeoutException("call timeout"); });
        return webAsyncTask;
    }
}

DeferredResult for Long‑Running Tasks

@RestController
public class AsyncDeferredController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    private final LongTimeTask taskService;
    @Autowired public AsyncDeferredController(LongTimeTask taskService) { this.taskService = taskService; }
    @GetMapping("/deferred")
    public DeferredResult<String> executeSlowTask() {
        logger.info(Thread.currentThread().getName() + " entering executeSlowTask");
        DeferredResult<String> deferredResult = new DeferredResult<>();
        taskService.execute(deferredResult);
        logger.info(Thread.currentThread().getName() + " returning from executeSlowTask");
        deferredResult.onTimeout(() -> { logger.info(Thread.currentThread().getName() + " onTimeout"); deferredResult.setErrorResult("time out!"); });
        deferredResult.onCompletion(() -> logger.info(Thread.currentThread().getName() + " onCompletion"));
        return deferredResult;
    }
}

AsyncHandlerInterceptor for Post‑Processing

@Component
public class MyAsyncHandlerInterceptor implements AsyncHandlerInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(MyAsyncHandlerInterceptor.class);
    @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { return true; }
    @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        logger.info(Thread.currentThread().getName() + " service call completed");
    }
    @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        if (ex != null) { System.out.println("Exception: " + ex.getMessage()); }
    }
    @Override public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String resp = "my name is chhliu!";
        response.setContentLength(resp.length());
        response.getOutputStream().write(resp.getBytes());
        logger.info(Thread.currentThread().getName() + " afterConcurrentHandlingStarted");
    }
}

These techniques allow Spring Boot applications to handle long‑running operations without blocking the main request thread, improving throughput and user experience.

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.

JavaAsynchronousCompletableFutureSpring BootWeb MVC
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

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.