skip to content

HandlerAdapter Invocation

HandlerAdapter abstracts how a handler is invoked, and the annotation-based one wires argument resolvers and return-value handlers around your method. This explains how Spring can pass you almost any parameter type you declare.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What is a HandlerAdapter in Spring MVC and why does DispatcherServlet need one?

level: juniorimportance: must knowfreq 55%

answer

  1. supports() + handle() → ModelAndView
  2. Adapter pattern, decouples DispatcherServlet
  3. RequestMappingHandlerAdapter is the usual one
  4. mapping = which, adapter = how
  5. null ModelAndView = response already written

basics

~10 s

A HandlerAdapter knows how to actually call a handler. DispatcherServlet finds the handler, then delegates to a HandlerAdapter to invoke it and get a ModelAndView, so it doesn't need to know each handler type.

solid answer

~40 s

After a HandlerMapping resolves which handler (usually a controller method) serves a request, DispatcherServlet doesn't call it directly. It asks each registered HandlerAdapter whether it supports(handler); the first that does is used to invoke it via handle(request, response, handler), returning a ModelAndView (possibly null). This indirection lets Spring support many handler styles behind one uniform contract: @RequestMapping methods (RequestMappingHandlerAdapter), plain Controller beans (SimpleControllerHandlerAdapter), HttpRequestHandler, functional endpoints. DispatcherServlet stays decoupled from any concrete handler shape — to support a new kind of handler you register a new HandlerAdapter, not modify the servlet. It is a textbook Adapter pattern: adapting heterogeneous handler objects to the single interface DispatcherServlet understands.

code

java · 14 lines
java
public interface HandlerAdapter {
    // Can this adapter invoke the given handler object?
    boolean supports(Object handler);

    // Invoke it; return a ModelAndView (null if response already written).
    ModelAndView handle(HttpServletRequest request,
                        HttpServletResponse response,
                        Object handler) throws Exception;
}

// Inside DispatcherServlet.doDispatch (paraphrased):
HandlerExecutionChain mappedHandler = getHandler(request);
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
ModelAndView mv = ha.handle(request, response, mappedHandler.getHandler());

go deeper

for a junior

Know that DispatcherServlet delegates invocation to a HandlerAdapter, and RequestMappingHandlerAdapter is the common one.

for a middle

Explain supports()/handle(), the Adapter pattern rationale, and the mapping-vs-adapter split.

for a senior

Discuss the concrete adapters, ordering, and null ModelAndView semantics for @ResponseBody.

for a principal

Frame it as an extension point (register custom handler types without touching DispatcherServlet) and relate to the overall dispatch lifecycle.

## The problem it solves Spring MVC's front controller is `DispatcherServlet`. For every request it runs a fixed pipeline: resolve a **handler** (via `HandlerMapping`), **invoke** that handler, then render the result. The catch is that a "handler" can be many different things: - An `@RequestMapping`/`@GetMapping` **method** on a `@Controller` (by far the most common). - A bean implementing the older `org.springframework.web.servlet.mvc.Controller` interface. - A bean implementing `HttpRequestHandler` (low-level access to `HttpServletRequest`/`HttpServletResponse`). - A functional `RouterFunction` endpoint. Each shape is invoked differently. Rather than putting `if (handler instanceof ...)` chains inside `DispatcherServlet`, Spring uses the **Adapter pattern** via the `HandlerAdapter` interface. ## The interface ```java public interface HandlerAdapter { boolean supports(Object handler); ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; // long getLastModified(...) — deprecated since 6.0 } ``` - `supports(handler)` — can this adapter invoke that handler object? - `handle(...)` — actually invoke it and return a `ModelAndView` (which may be `null` when the response is already fully written, e.g. `@ResponseBody`). ## How DispatcherServlet uses it Inside `DispatcherServlet.doDispatch(...)` the flow is roughly: 1. `mappedHandler = getHandler(request)` — a `HandlerExecutionChain` (handler + interceptors). 2. `HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler())` — loops over the `handlerAdapters` list and returns the first whose `supports()` is true; throws `ServletException` if none match. 3. Applies interceptor `preHandle`. 4. `mv = ha.handle(request, response, handler)` — the actual invocation. 5. Applies `postHandle`, then renders the `ModelAndView`. ## The concrete adapters - **`RequestMappingHandlerAdapter`** — invokes annotated `@RequestMapping` handler methods; where argument resolvers and return-value handlers live. The one you almost always use. - **`HttpRequestHandlerAdapter`** — for `HttpRequestHandler` beans. - **`SimpleControllerHandlerAdapter`** — for beans implementing `Controller`. - **`HandlerFunctionAdapter`** — for functional (`RouterFunction`) endpoints. With Spring Boot / `@EnableWebMvc`, these are auto-registered and ordered. ## Gotchas - The `handle` return of `null` is normal, not an error — it signals the response was already written (REST/`@ResponseBody`), so no view rendering is needed. - `HandlerMapping` and `HandlerAdapter` are **separate** responsibilities: mapping decides *which* handler, adapter decides *how to call it*. Candidates often conflate them. - The first matching adapter wins, so ordering matters, but in practice each `supports()` is specific enough that there is no overlap. ## When to care Day to day you never touch this — it's plumbing. You care when you register a custom handler type, debug a `"No adapter for handler"` `ServletException`, or explain the request lifecycle in an interview.

  • What is the difference between a HandlerMapping and a HandlerAdapter?
    HandlerMapping resolves WHICH handler (and interceptors) serves a URL, returning a HandlerExecutionChain. HandlerAdapter knows HOW to invoke that handler. They are distinct pipeline stages: mapping first, adapter second.
  • What happens if no HandlerAdapter supports the resolved handler?
    DispatcherServlet.getHandlerAdapter throws a ServletException 'No adapter for handler ...: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler'.

saying these in an interview costs you the question

  • Saying DispatcherServlet calls the controller method directly (it delegates to a HandlerAdapter).
  • Confusing HandlerMapping (which handler) with HandlerAdapter (how to invoke it).
  • Claiming handle() must always return a non-null ModelAndView.

context

open as a page

Walk through what RequestMappingHandlerAdapter.invokeHandlerMethod does when a @RequestMapping method is called.

level: middleimportance: must knowfreq 45%

basics

~20 s

It wraps the controller method in a ServletInvocableHandlerMethod, plugs in the argument resolvers and return-value handlers, binds a ModelAndViewContainer, resolves each parameter, invokes the method, and hands the return value to the right return-value handler.

open as a page

How do HandlerMethodArgumentResolver and HandlerMethodReturnValueHandler work, and how does the adapter pick the right one?

level: seniorimportance: should knowfreq 40%

basics

~20 s

For each parameter the adapter asks every argument resolver supportsParameter; the first match resolves the value. For the return value it asks every return-value handler supportsReturnType; the first match writes the response or sets the view. Both are strategy lists tried in order.

open as a page

For a @RestController method returning a DTO, why does HandlerAdapter.handle ultimately return a null ModelAndView, and where is the JSON actually written?

level: seniorimportance: should knowfreq 30%

basics

~10 s

@ResponseBody sends the return value to RequestResponseBodyMethodProcessor, which serializes it with an HttpMessageConverter and marks the request as handled. Because the response is already written, no view is needed, so getModelAndView returns null.

open as a page

How does RequestMappingHandlerAdapter handle async return types (Callable, DeferredResult), and why is the HandlerAdapter abstraction valuable architecturally?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

For async return types the adapter starts async processing via WebAsyncManager, returns a null ModelAndView, and lets the servlet container release the thread. When the result is ready the request is re-dispatched and only the return-value handling re-runs. The abstraction keeps DispatcherServlet closed to modification but open to new handler types.

open as a page