What is a HandlerAdapter in Spring MVC and why does DispatcherServlet need one?
answer
- supports() + handle() → ModelAndView
- Adapter pattern, decouples DispatcherServlet
- RequestMappingHandlerAdapter is the usual one
- mapping = which, adapter = how
- null ModelAndView = response already written
basics
~10 sA 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 sAfter 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 linespublic 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
Know that DispatcherServlet delegates invocation to a HandlerAdapter, and RequestMappingHandlerAdapter is the common one.
Explain supports()/handle(), the Adapter pattern rationale, and the mapping-vs-adapter split.
Discuss the concrete adapters, ordering, and null ModelAndView semantics for @ResponseBody.
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.