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