Spring MVC Request Lifecycle: HandlerMappings, HandlerAdapters, and MessageConverters
Deep-dive into RequestMappingHandlerMapping, PathPatternParser, ArgumentResolvers, and Jackson serialization
Part 12 in Series — Catch up on the previous article: The Front Controller Pattern: How DispatcherServlet Routes HTTP Requests (Part 11) before diving into this post.
Why You Need This in Real Life
A financial trading REST API processes stock buy orders through annotated controller signatures:
@PostMapping("/api/v1/orders/{symbol}")
public ResponseEntity<OrderResponse> executeTrade(
@PathVariable String symbol,
@Valid @RequestBody CreateOrderRequest request,
@RequestHeader("X-Trader-ID") String traderId) {
return ResponseEntity.ok(orderService.placeOrder(symbol, request, traderId));
}
Under high load, your microservice throws intermittent HTTP 400 Bad Request errors. When client applications submit JSON payloads with trailing commas or timestamps formatted as epoch milliseconds (1725800000000) instead of ISO-8601 strings (2026-09-08T18:00:00Z), Jackson throws InvalidDefinitionException or HttpMessageNotReadableException. Furthermore, high-throughput requests experience elevated latency due to URL pattern matching overhead on wildcard routes.
To build zero-bug REST APIs, you must understand the exact processing steps between an incoming HTTP byte stream and your Java @Controller parameters.
Part 1: Routing URIs with HandlerMapping
When DispatcherServlet receives a request, it iterates over registered HandlerMapping implementations to locate the corresponding handler.
RequestMappingHandlerMapping Registry
During application startup, RequestMappingHandlerMapping scans all @Controller and @RestController beans. It inspects @RequestMapping, @GetMapping, @PostMapping annotations and constructs an internal lookup table mapping RequestMappingInfo (URL patterns, HTTP methods, headers, media types) to HandlerMethod references.
Incoming Request: POST /api/v1/orders/AAPL
|
v
+-------------------------------------------------------------+
| RequestMappingHandlerMapping |
| |
| Registry Lookup: |
| GET /api/v1/orders/{symbol} -> OrderController.getOrder() |
| POST /api/v1/orders/{symbol} -> OrderController.execute() | <--- MATCH!
+------------------------------+------------------------------+
|
v
Returns HandlerExecutionChain containing:
1. HandlerMethod: OrderController.executeTrade()
2. List<HandlerInterceptor>: [AuthInterceptor, LoggingInterceptor]
Routing Matching Engines: AntPathMatcher vs PathPatternParser
AntPathMatcher(Legacy Default): Parses routes string-by-string on every request using Ant-style regex patterns (/orders/**). It relies on heavy string splitting and can become a latency bottleneck under high throughput.PathPatternParser(Spring Boot 2.6+ Default): Parses URL patterns into a compiled tree structure ofPathElementnodes (LiteralPathElement,SinglePathSegmentVariablePathElement). Route matching is performed via fast tree traversal, executing up to 2x faster thanAntPathMatcher.
Part 2: Invoking Methods via HandlerAdapter
A HandlerMethod is just metadata (a Class pointer, a Method reference, and parameter details). It cannot execute itself. DispatcherServlet uses HandlerAdapter to execute the target method.
For annotation-based controllers, Spring uses RequestMappingHandlerAdapter.
HandlerAdapter.handle()
|
v
+------------------------------------+
| RequestMappingHandlerAdapter |
+------------------+-----------------+
|
v
+------------------------------------+
| InvocableHandlerMethod.invokeAndHandle()
+------------------+-----------------+
|
+----------------------+----------------------+
| |
v v
1. Resolve Method Arguments 2. Handle Return Value
(HandlerMethodArgumentResolver) (HandlerMethodReturnValueHandler)
Part 3: Parameter Extraction with HandlerMethodArgumentResolver
RequestMappingHandlerAdapter contains a chain of HandlerMethodArgumentResolver instances. For every parameter in your controller signature, Spring iterates through this chain calling supportsParameter(MethodParameter parameter).
| Annotation / Type | Dedicated HandlerMethodArgumentResolver | Responsibility |
|---|---|---|
@PathVariable | PathVariableMethodArgumentResolver | Extracts URI path variables from ServletRequestAttributes. |
@RequestParam | RequestParamMethodArgumentResolver | Extracts query parameters and application/x-www-form-urlencoded fields. |
@RequestHeader | RequestHeaderMethodArgumentResolver | Reads HTTP headers from HttpServletRequest. |
@RequestBody | RequestResponseBodyMethodProcessor | Reads HTTP request input stream and invokes HttpMessageConverter. |
HttpServletRequest | ServletRequestMethodArgumentResolver | Supplies raw Servlet API objects. |
Deep-Dive: How @RequestBody Works
When RequestResponseBodyMethodProcessor resolves a @RequestBody parameter:
- It reads the HTTP
Content-Typeheader (e.g.,application/json). - It inspects all configured
HttpMessageConverterimplementations to find one wherecanRead(targetType, mediaType)returnstrue. - For JSON payloads,
MappingJackson2HttpMessageConverterdelegates to Jackson’sObjectMapper.readValue(InputStream, Class). - If Java Bean Validation (
@Validor@Validated) is present, Spring’sSmartValidatorexecutes constraints on the target object. If validation fails, it throwsMethodArgumentNotValidException.
Part 4: Serializing Responses with HttpMessageConverter
When a controller method returns an object (e.g., OrderResponse) and is annotated with @ResponseBody (or defined inside a @RestController), RequestResponseBodyMethodProcessor handles the return value.
Controller Return Object (e.g., OrderResponse)
|
v
+-------------------------------------------------------------+
| RequestResponseBodyMethodProcessor |
| |
| 1. Inspect Accept Header (e.g., application/json) |
| 2. Select MappingJackson2HttpMessageConverter |
| 3. Set HTTP Content-Type: application/json |
| 4. Invoke ObjectMapper.writeValue(response.getOutputStream())|
+-------------------------------------------------------------+
|
v
HTTP Response Body (JSON Byte Stream)
Part 5: Production Edge Cases & Jackson Gotchas
Gotcha 1: Infinite Recursion with Bidirectional JPA Relationships
If entity classes have bidirectional @OneToMany and @ManyToOne relationships, Jackson’s default serializer will enter infinite recursion trying to serialize child objects back to parent objects until the JVM crashes with StackOverflowError.
// WRONG: Jackson StackOverflowError
@Entity
public class Order {
@OneToMany(mappedBy = "order")
private List<OrderItem> items;
}
@Entity
public class OrderItem {
@ManyToOne
private Order order; // Causes Infinite JSON Loop!
}
- Solution: Use DTO projections (recommended), or annotate fields with
@JsonManagedReference(parent side) and@JsonBackReference(child side), or@JsonIgnore.
Gotcha 2: Silent Date Truncation & Timezone Drift
By default, Jackson serializes java.util.Date or java.time.LocalDateTime into epoch timestamps or ISO-8601 strings using the server’s local timezone. When deployed across AWS regions, timestamp formats diverge.
- Solution: Configure explicit Jackson date serialization in
application.properties:spring.jackson.date-format=yyyy-MM-dd'T'HH:mm:ss.SSSXXX spring.jackson.time-zone=UTC spring.jackson.serialization.write-dates-as-timestamps=false
Next Steps
Now that we understand how requests are routed, parameters resolved, and payloads serialized, we will examine how exceptions during this lifecycle are trapped and handled using @ControllerAdvice and @ExceptionHandler.
References & Further Reading
- IETF. RFC 7807 — Problem Details for HTTP APIs. Internet Engineering Task Force.
- Richardson, L., & Ruby, S. (2007). RESTful Web Services. O’Reilly Media.
- Spring.io. Spring Framework REST Controllers & Exception Handling (
@RestControllerAdvice). Spring Docs.
Part 13: Exception Handling in Spring Web: ControllerAdvice, ExceptionHandlers, and Error Responses
Continue to Part 13 →