Adetayo Akinsanya unkletayo.dev

Spring MVC Request Lifecycle: HandlerMappings, HandlerAdapters, and MessageConverters

Deep-dive into RequestMappingHandlerMapping, PathPatternParser, ArgumentResolvers, and Jackson serialization

Adetayo Akinsanya (unkletayo) 2026-09-25

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 of PathElement nodes (LiteralPathElement, SinglePathSegmentVariablePathElement). Route matching is performed via fast tree traversal, executing up to 2x faster than AntPathMatcher.

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 / TypeDedicated HandlerMethodArgumentResolverResponsibility
@PathVariablePathVariableMethodArgumentResolverExtracts URI path variables from ServletRequestAttributes.
@RequestParamRequestParamMethodArgumentResolverExtracts query parameters and application/x-www-form-urlencoded fields.
@RequestHeaderRequestHeaderMethodArgumentResolverReads HTTP headers from HttpServletRequest.
@RequestBodyRequestResponseBodyMethodProcessorReads HTTP request input stream and invokes HttpMessageConverter.
HttpServletRequestServletRequestMethodArgumentResolverSupplies raw Servlet API objects.

Deep-Dive: How @RequestBody Works

When RequestResponseBodyMethodProcessor resolves a @RequestBody parameter:

  1. It reads the HTTP Content-Type header (e.g., application/json).
  2. It inspects all configured HttpMessageConverter implementations to find one where canRead(targetType, mediaType) returns true.
  3. For JSON payloads, MappingJackson2HttpMessageConverter delegates to Jackson’s ObjectMapper.readValue(InputStream, Class).
  4. If Java Bean Validation (@Valid or @Validated) is present, Spring’s SmartValidator executes constraints on the target object. If validation fails, it throws MethodArgumentNotValidException.

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

  1. IETF. RFC 7807 — Problem Details for HTTP APIs. Internet Engineering Task Force.
  2. Richardson, L., & Ruby, S. (2007). RESTful Web Services. O’Reilly Media.
  3. Spring.io. Spring Framework REST Controllers & Exception Handling (@RestControllerAdvice). Spring Docs.

Up Next in Series →

Part 13: Exception Handling in Spring Web: ControllerAdvice, ExceptionHandlers, and Error Responses

Continue to Part 13 →