Spring MVC
@Async
Java
Asynchronous Programming
Spring Framework

Spring MVC and Async

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Spring MVC supports asynchronous request handling, which helps web applications stay responsive when work takes longer than a normal request cycle. Teams often mix up MVC async request processing and @Async background execution, but they solve different parts of the problem. This guide shows how they fit together and how to configure them safely.

Core Topic Sections

Two async models in Spring web applications

Spring provides two related but distinct patterns:

  1. MVC async request processing with Callable, DeferredResult, or WebAsyncTask.
  2. Method-level async execution with @Async on service methods.

MVC async frees the request thread while waiting for work. @Async runs work on a task executor. In many systems you use both.

MVC async with Callable

A controller can return Callable so the servlet container thread is released quickly, and response generation resumes when callable completes.

java
1import org.springframework.web.bind.annotation.GetMapping;
2import org.springframework.web.bind.annotation.RestController;
3
4import java.util.concurrent.Callable;
5
6@RestController
7public class ReportController {
8
9    @GetMapping("/report")
10    public Callable<String> report() {
11        return () -> {
12            Thread.sleep(1000);
13            return "report-ready";
14        };
15    }
16}

This pattern is simple and works well for bounded tasks.

MVC async with DeferredResult

DeferredResult is useful when completion is driven by another component or callback.

java
1import org.springframework.web.bind.annotation.GetMapping;
2import org.springframework.web.bind.annotation.RestController;
3import org.springframework.web.context.request.async.DeferredResult;
4
5import java.util.concurrent.ExecutorService;
6import java.util.concurrent.Executors;
7
8@RestController
9public class JobController {
10
11    private final ExecutorService pool = Executors.newFixedThreadPool(4);
12
13    @GetMapping("/job")
14    public DeferredResult<String> job() {
15        DeferredResult<String> result = new DeferredResult<>(3000L);
16
17        pool.submit(() -> {
18            try {
19                Thread.sleep(1200);
20                result.setResult("done");
21            } catch (Exception ex) {
22                result.setErrorResult("failed");
23            }
24        });
25
26        result.onTimeout(() -> result.setErrorResult("timeout"));
27        return result;
28    }
29}

This gives explicit timeout and lifecycle hooks.

@Async service layer execution

Use @Async for background business logic that should run on a managed executor.

java
1import org.springframework.scheduling.annotation.Async;
2import org.springframework.stereotype.Service;
3
4import java.util.concurrent.CompletableFuture;
5
6@Service
7public class MailService {
8
9    @Async("appExecutor")
10    public CompletableFuture<String> sendEmail(String to) {
11        // simulate expensive IO
12        try {
13            Thread.sleep(800);
14        } catch (InterruptedException ignored) {
15        }
16        return CompletableFuture.completedFuture("sent to " + to);
17    }
18}

Controller can return CompletableFuture directly in modern Spring versions, which integrates cleanly with async response handling.

Configure executor and async support

Default executors are not ideal for production. Configure thread pool sizes and queue limits explicitly.

java
1import org.springframework.context.annotation.Bean;
2import org.springframework.context.annotation.Configuration;
3import org.springframework.scheduling.annotation.EnableAsync;
4import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
5
6import java.util.concurrent.Executor;
7
8@Configuration
9@EnableAsync
10public class AsyncConfig {
11
12    @Bean(name = "appExecutor")
13    public Executor appExecutor() {
14        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
15        executor.setCorePoolSize(8);
16        executor.setMaxPoolSize(16);
17        executor.setQueueCapacity(200);
18        executor.setThreadNamePrefix("app-async-");
19        executor.initialize();
20        return executor;
21    }
22}

Tune values with load tests rather than guesses.

Timeouts, errors, and observability

Async code fails differently from synchronous handlers. Always define:

  1. Request timeout behavior.
  2. Exception mapping for async failures.
  3. Metrics for queue depth and execution time.
  4. Correlation identifiers in logs.

Without these controls, async endpoints become hard to debug under load.

Avoid blocking mistakes

Common anti-pattern is marking controller async while still doing blocking network calls on a small thread pool. Async architecture only helps when the full flow is designed for concurrency.

Practical rules:

  1. Keep blocking operations in appropriately sized worker pools.
  2. Avoid sharing tiny executors across unrelated workloads.
  3. Apply backpressure and reject policies for overload protection.

Testing strategy

Test async behavior, not only response body:

  1. Verify timeout responses.
  2. Verify error mapping from background failures.
  3. Verify that thread pools do not exhaust in load tests.

Include integration tests with realistic delays to validate operational behavior.

Common Pitfalls

  • Assuming @Async alone makes an MVC endpoint non-blocking end to end.
  • Using default executors in production without capacity planning.
  • Missing timeout handling and leaving requests hanging.
  • Ignoring async exception mapping and losing useful error diagnostics.
  • Running blocking work in undersized pools and creating thread starvation.

Summary

  • Spring MVC async request handling and @Async address different layers.
  • Use Callable or DeferredResult to free request threads.
  • Use managed executors with explicit sizing for background tasks.
  • Define timeout, error, and observability policies early.
  • Validate async behavior with integration and load testing.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.