The Hidden Pitfall of Passing HttpServletRequest to Async Threads in Spring Boot
Passing an HttpServletRequest into an asynchronous thread in Spring Boot can cause mysterious parameter loss and request‑recycling bugs, because Tomcat reuses request objects; the proper solution is to employ the servlet's startAsync and AsyncContext.complete methods for safe async processing.
Problem Overview
When a HttpServletRequest is handed over to a thread pool in a Spring Boot application, developers may observe that request parameters disappear or that subsequent requests throw "missing required parameter" errors. The root cause is Tomcat's reuse of Request objects after the original servlet method returns.
Reproducing the Issue
The following minimal controller demonstrates the problem:
@GetMapping("/getTest")
public String getTest(HttpServletRequest request) {
String age = request.getParameter("age");
System.out.println("age=" + age);
new Thread(() -> {
try { Thread.sleep(200); } catch (InterruptedException e) { throw new RuntimeException(e); }
String age1 = request.getParameter("age");
System.out.println("age1=" + age1);
}).start();
return "success";
}Calling http://127.0.0.1:8080/getTest?age=18 prints the age in the main thread, but the asynchronous thread often prints null on the second request.
Debugging Findings
By setting the logging level to debug and tracing the call stack, the following observations were made:
The first request triggers Parameters.processParameters, which parses the query string and stores values in a map.
After the servlet method finishes, Tomcat calls Request.recycle, clearing the map and resetting the flag didQueryParameters to false.
If an asynchronous thread accesses request.getParameter after the recycle, the flag may already be true, causing the method to skip parsing and return null.
Tomcat Request Lifecycle
The Servlet specification states that a request object is valid only within the servlet's service method unless asynchronous processing is started with request.startAsync(). Containers typically recycle request objects to avoid allocation overhead, which explains the observed behavior.
Key excerpts from the spec:
Each request object is valid only within the scope of a servlet’s service method, unless the asynchronous processing is enabled for the component and the startAsync method is invoked on the request object.
In the case where asynchronous processing occurs, the request object remains valid until complete is invoked on the AsyncContext.
Containers commonly recycle request objects in order to avoid the performance overhead of request object creation.Correct Asynchronous Handling
To safely use a request in another thread, start asynchronous processing explicitly and complete it when the work is done:
@GetMapping("/getTest")
public void getTest(HttpServletRequest request, HttpServletResponse response) throws IOException {
AsyncContext async = request.startAsync();
async.start(() -> {
try {
String age = request.getParameter("age");
// business logic here
response.getWriter().write("age=" + age);
} catch (Exception e) {
e.printStackTrace();
} finally {
async.complete();
}
});
}Using startAsync keeps the request alive beyond the servlet method, prevents premature recycling, and guarantees that AsyncContext.complete() will finally release the resources.
In summary, never pass a HttpServletRequest to a plain thread pool; always use the servlet asynchronous API ( startAsync / AsyncContext.complete) to avoid the hidden request‑recycling bug.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
