Close

Spring MVC - AsyncHandlerInterceptor Examples

[Last Updated: Aug 16, 2026]

Spring MVC 

HandlerInterceptor#afterConcurrentHandlingStarted() method is invoked just before main request thread exists after spawning a new thread for async processing.

package com.logicbig.example;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import java.time.LocalTime;


public class MyAsyncHandlerInterceptor
implements AsyncHandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {

log("interceptor#preHandle", request);
return true;

}

@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {

log("interceptor#postHandle", request);
}

@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {

log("interceptor#afterCompletion", request);
}

@Override
public void afterConcurrentHandlingStarted(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {

log("interceptor#afterConcurrentHandlingStarted", request);
}

private static void log(String stage,
HttpServletRequest request) {
System.out.printf("%s %s called. "
+ "Thread: %s, "
+ "DispatcherType: %s%n",
LocalTime.now(),
stage,
Thread.currentThread().getName(),
request.getDispatcherType());
}
}

package com.logicbig.example;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.time.LocalTime;
import java.util.concurrent.Callable;

@Controller
public class MyController {
@RequestMapping("/test")
@ResponseBody
public Callable<String> handleTestRequest() {

log("controller#handler called");

Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
log("controller-callable#async task started");
Thread.sleep(300);
log("controller-callable#async task finished");
return "async result";
}
};

log("controller#handler finished");
return callable;
}

private static void log(String msg) {
System.out.println(
LocalTime.now() +
" MyController [" +
Thread.currentThread().getName() + "] "
+ msg);
}
}

package com.logicbig.example;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.servlet.config.annotation.*;

@Configuration
@EnableWebMvc
@ComponentScan
public class WebConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyAsyncHandlerInterceptor());
}

@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setThreadNamePrefix("mvc-async-");
executor.initialize();
configurer.setTaskExecutor(executor);
}
}

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.logicbig.example</groupId>
<artifactId>async-handler-interceptor-example</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>7.0.6</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>7.0.6</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>6.0.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.26.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.15.0</version>
<configuration>
<source>25</source>
<target>25</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.jetty.ee11</groupId>
<artifactId>jetty-ee11-maven-plugin</artifactId>
<version>12.1.6</version>
<configuration>
<webApp>
<contextPath>/</contextPath>
</webApp>
<stopPort>9966</stopPort>
<stopKey>foo</stopKey>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original Post




This example uses DeferredResult instead of Callable for invoking a long running task.


package com.logicbig.example;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class MyAsyncHandlerInterceptor extends HandlerInterceptorAdapter {

@Override
public boolean preHandle (HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {

System.out.println("interceptor#preHandle called. Thread: " + Thread
.currentThread().getName());
return true;

}

@Override
public void postHandle (HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {

System.out.println("interceptor#postHandle called. Thread: " +
Thread.currentThread()
.getName());
}

@Override
public void afterCompletion (HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) throws Exception {

System.out.println("interceptor#afterCompletion called Thread.: " +
Thread.currentThread()
.getName());
}

@Override
public void afterConcurrentHandlingStarted (HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {

System.out.println("interceptor#afterConcurrentHandlingStarted called. " +
"Thread: " +
Thread.currentThread()
.getName());
}
}


package com.logicbig.example;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.async.DeferredResult;

import java.util.concurrent.Callable;

@Controller
public class MyWebController {
@RequestMapping("/")
@ResponseBody
public DeferredResult<String> handleTestRequest () {

System.out.println("controller#handler called. Thread: " +
Thread.currentThread()
.getName());

final DeferredResult<String> deferredResult = new DeferredResult<>();

new Thread(() -> {
System.out.println("controller-deferred#async task started. Thread: " +
Thread.currentThread()
.getName());
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
deferredResult.setResult("test async result");
System.out.println("controller-deferred#async task finished");
}).start();


System.out.println("controller#handler finished");
return deferredResult;
}
}


package com.logicbig.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

public class Main {
public static void main (String[] args) {
SpringApplication.run(BootApplication.class, args);
}

@SpringBootApplication
public static class BootApplication extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors (InterceptorRegistry registry) {
registry.addInterceptor(new MyAsyncHandlerInterceptor());
}
}
}

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.logicbig.example</groupId>
<artifactId>async-deferred-handler-interceptor</artifactId>
<version>1.0-SNAPSHOT</version>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.2.RELEASE</version>
</parent>

<properties>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>




See Also

Join