skip to content

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