Backend Development 10 min read

Understanding Asynchronous Patterns in Spring MVC: DeferredResult, WebAsyncTask, Timeout and Exception Handling

This article explains the difference between synchronous and asynchronous request handling in web servers, demonstrates how Spring MVC leverages DeferredResult and WebAsyncTask for non‑blocking processing, and shows practical code for timeout and exception management in Java backend applications.

Qunar Tech Salon
Qunar Tech Salon
Qunar Tech Salon
Understanding Asynchronous Patterns in Spring MVC: DeferredResult, WebAsyncTask, Timeout and Exception Handling

To grasp asynchronous patterns you first need to understand the traditional synchronous model where a browser request is handled by a dedicated server thread that blocks until the response is ready, which can exhaust the thread pool under heavy load.

In the asynchronous model the request‑handling thread invokes a backend service using an invoke style call and immediately returns, freeing the thread to serve other requests; a separate callback thread later processes the result and sends the response, improving server throughput.

Spring MVC asynchronous support requires Servlet 3.0+; the Maven dependencies are:

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.1.0</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>4.2.3.RELEASE</version>
</dependency>

Using DeferredResult<ModelAndView> the controller can return immediately while a simulated long‑running service ( LongTimeAsyncCallService ) performs work in a scheduled thread pool and invokes a callback when finished:

public interface LongTermTaskCallback {
    void callback(Object result);
}
public class LongTimeAsyncCallService {
    private final int CorePoolSize = 4;
    private final int NeedSeconds = 3;
    private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(CorePoolSize);
    public void makeRemoteCallAndUnknownWhenFinish(LongTermTaskCallback callback){
        System.out.println("完成此任务需要 : " + NeedSeconds + " 秒");
        scheduler.schedule(() -> callback.callback("长时间异步调用完成."), 3, TimeUnit.SECONDS);
    }
}

The controller method looks like:

@RequestMapping(value="/asynctask", method = RequestMethod.GET)
public DeferredResult
asyncTask(){
    DeferredResult
deferredResult = new DeferredResult<>();
    System.out.println("/asynctask 调用!thread id is : " + Thread.currentThread().getId());
    longTimeAsyncCallService.makeRemoteCallAndUnknownWhenFinish(result -> {
        System.out.println("异步调用执行完成, thread id is : " + Thread.currentThread().getId());
        ModelAndView mav = new ModelAndView("remotecalltask");
        mav.addObject("result", result);
        deferredResult.setResult(mav);
    });
    return deferredResult;
}

Another option is WebAsyncTask , which wraps a Callable<ModelAndView> . The callable runs in a separate worker thread, and the task can be created with a timeout value:

@RequestMapping(value="/longtimetask", method = RequestMethod.GET)
public WebAsyncTask longTimeTask(){
    System.out.println("/longtimetask被调用 thread id is : " + Thread.currentThread().getId());
    Callable
callable = () -> {
        Thread.sleep(3000);
        ModelAndView mav = new ModelAndView("longtimetask");
        mav.addObject("result", "执行成功");
        System.out.println("执行成功 thread id is : " + Thread.currentThread().getId());
        return mav;
    };
    return new WebAsyncTask(callable);
}

To handle timeouts with WebAsyncTask , create the task with a shorter timeout and register an onTimeout callback that returns an alternative view:

WebAsyncTask asyncTask = new WebAsyncTask(2000, callable);
asyncTask.onTimeout(() -> {
    ModelAndView mav = new ModelAndView("longtimetask");
    mav.addObject("result", "执行超时");
    System.out.println("执行超时 thread id is :" + Thread.currentThread().getId());
    return mav;
});
return asyncTask;

Similarly, DeferredResult can be given a timeout by constructing it with a timeout value and registering an onTimeout runnable that sets a timeout response.

DeferredResult
deferredResult = new DeferredResult<>(2000L);
// ... same async call as before ...
deferredResult.onTimeout(() -> {
    System.out.println("异步调用执行超时!thread id is : " + Thread.currentThread().getId());
    ModelAndView mav = new ModelAndView("remotecalltask");
    mav.addObject("result", "异步调用执行超时");
    deferredResult.setResult(mav);
});

Exception handling remains unchanged; a standard @ExceptionHandler method can return an error view:

@ExceptionHandler(Exception.class)
public ModelAndView handleAllException(Exception ex) {
    ModelAndView model = new ModelAndView("error");
    model.addObject("result", ex.getMessage());
    return model;
}

These patterns allow Spring MVC applications to avoid thread blockage, improve throughput, and gracefully handle long‑running operations, timeouts, and errors.

Spring MVCDeferredResultWebAsyncTask
Qunar Tech Salon
Written by

Qunar Tech Salon

Qunar Tech Salon is a learning and exchange platform for Qunar engineers and industry peers. We share cutting-edge technology trends and topics, providing a free platform for mid-to-senior technical professionals to exchange and learn.

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.