skip to content

Argument & Return-Value Handlers

HandlerMethodArgumentResolver and HandlerMethodReturnValueHandler are the extension points behind every supported parameter and return type, and you can register your own. The idiomatic answer to 'how would you inject the current user into every controller method'.

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

questions

6

How does Spring MVC decide what value to pass to each parameter of a @RequestMapping controller method?

level: juniorimportance: must knowfreq 70%

answer

  1. supportsParameter -> resolveArgument
  2. first match wins in a composite
  3. RequestMappingHandlerAdapter holds the lists
  4. ModelAndViewContainer carries model + view
  5. return side = HandlerMethodReturnValueHandler

basics

~20 s

For every controller-method parameter Spring asks a list of HandlerMethodArgumentResolver strategies which one can handle it. The first that says yes resolves the value (from a request param, path variable, body, etc.) and passes it in.

solid answer

~30 s

Spring MVC controller methods have flexible signatures because RequestMappingHandlerAdapter holds an ordered list of HandlerMethodArgumentResolver strategies (a composite). For each MethodParameter it calls supportsParameter until one returns true, then that resolver's resolveArgument produces the value. Built-ins cover @RequestParam, @PathVariable, @RequestHeader, @RequestBody, @ModelAttribute, Model/Map, HttpServletRequest, Principal, RedirectAttributes, and more. Symmetrically, HandlerMethodReturnValueHandler strategies process the return value (view name, @ResponseBody, ModelAndView, etc.). This strategy-based design is why you can add or reorder parameters freely and why the framework is extensible: you can plug in your own resolver/handler without changing controller code.

code

java · 16 lines
java
// The two strategy interfaces Spring MVC iterates for every handler method.
public interface HandlerMethodArgumentResolver {
    boolean supportsParameter(MethodParameter parameter);
    Object resolveArgument(MethodParameter parameter,
                           ModelAndViewContainer mavContainer,
                           NativeWebRequest webRequest,
                           WebDataBinderFactory binderFactory) throws Exception;
}

public interface HandlerMethodReturnValueHandler {
    boolean supportsReturnType(MethodParameter returnType);
    void handleReturnValue(Object returnValue,
                           MethodParameter returnType,
                           ModelAndViewContainer mavContainer,
                           NativeWebRequest webRequest) throws Exception;
}

go deeper

for a junior

Know the two interfaces exist and that Spring picks the first resolver that supports a parameter.

for a middle

Explain the composite, first-match ordering, ModelAndViewContainer, and name several built-in resolvers.

for a senior

Discuss RequestMappingHandlerAdapter wiring, catch-all ordering, and dual-SPI processors like RequestResponseBodyMethodProcessor.

for a principal

Reason about extensibility, resolver caching, and how this SPI keeps controller signatures decoupled from transport concerns.

### The problem this solves A Spring MVC controller method can declare almost any parameter list — `@RequestParam String q`, `@PathVariable Long id`, `@RequestBody Order body`, `Model model`, `HttpServletRequest req`, `Principal user`, `RedirectAttributes ra`. Something has to look at each parameter, figure out where its value comes from, and produce it before the method is invoked. That something is the **argument-resolver** subsystem. ### The core SPI Spring defines two strategy interfaces (SPIs = Service Provider Interfaces, i.e. extension points): - **`HandlerMethodArgumentResolver`** — resolves an incoming parameter value: - `boolean supportsParameter(MethodParameter parameter)` — can I handle this parameter? - `Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)` — produce the value. - **`HandlerMethodReturnValueHandler`** — processes what the method returns: - `boolean supportsReturnType(MethodParameter returnType)` - `void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)` `MethodParameter` is Spring's reflection wrapper that carries the parameter's type, index, annotations, and generic info. ### Where they live and how they run `RequestMappingHandlerAdapter` (the adapter that actually invokes `@RequestMapping` methods) builds two composites at startup: a `HandlerMethodArgumentResolverComposite` and a `HandlerMethodReturnValueHandlerComposite`. Each composite holds an **ordered list**. When a request arrives: 1. For each method parameter, the composite iterates its resolvers, calling `supportsParameter`; the **first** that returns `true` wins and its `resolveArgument` runs (results are cached per parameter for speed). 2. The method is invoked with the resolved args. 3. The return value is handed to the return-value composite, which finds the first handler whose `supportsReturnType` is true and calls `handleReturnValue`. ### The shared context object Both sides get a **`ModelAndViewContainer`** — a mutable bag that carries the model attributes and the chosen view (or a `@ResponseBody`-was-handled flag) across resolution and handling. For example the `Model` argument resolver hands the controller the container's model; a view-name return handler sets the view on the same container. ### Representative built-in resolvers - `RequestParamMethodArgumentResolver` — `@RequestParam`, and (in catch-all mode) simple types. - `PathVariableMethodArgumentResolver` — `@PathVariable`. - `RequestHeaderMethodArgumentResolver` — `@RequestHeader`. - `RequestResponseBodyMethodProcessor` — `@RequestBody` / `@ResponseBody` (implements BOTH SPIs; delegates to `HttpMessageConverter`s). - `ServletModelAttributeMethodProcessor` — `@ModelAttribute` and, in catch-all mode, complex objects (data binding). - `ModelMethodProcessor` / `MapMethodProcessor` — `Model`, `ModelMap`, `Map` parameters. - `RedirectAttributesMethodArgumentResolver` — `RedirectAttributes`. - `ServletRequestMethodArgumentResolver` — `HttpServletRequest`, `HttpSession`, `Principal`, `Locale`, etc. ### Gotchas / edge cases - **First-match wins**: order matters. The last two default resolvers are *catch-alls* (simple types → request param; everything else → model attribute), so an unannotated `String` becomes a request param and an unannotated object gets data-bound. - Some processors implement **both** SPIs (`RequestResponseBodyMethodProcessor`, `ModelAttributeMethodProcessor`), so the same class appears in both composites. - Resolution happens **before** the method runs; a resolver can throw (e.g. `MissingServletRequestParameterException`) and short-circuit into exception handling. ### When to care Day to day you rely on built-ins. You reach for this knowledge when you need a **custom parameter** (e.g. inject the current tenant or a decoded principal object) or a **custom return type** — then you implement the SPI and register it.

  • If a controller parameter has no annotation and is a simple type like String, how is it resolved?
    By the catch-all RequestParamMethodArgumentResolver (configured with useDefaultResolution=true), which is placed last, so an unannotated simple type is treated as a @RequestParam by its name.
  • What object lets the argument side and the return side communicate?
    ModelAndViewContainer — it carries the model attributes and the selected view/redirect state across resolvers, the handler invocation, and return-value handlers.

saying these in an interview costs you the question

  • Thinking Spring uses reflection parameter names alone with no strategy layer
  • Believing all resolvers run for every parameter (only the first supporting one runs)
  • Confusing HandlerMethodArgumentResolver with HandlerInterceptor or Filter

context

open as a page

What does RedirectAttributes do, and what is the difference between addAttribute and addFlashAttribute?

level: middleimportance: must knowfreq 60%

basics

~20 s

RedirectAttributes controls data passed through a redirect. addAttribute puts values in the redirect URL (path/query params, visible). addFlashAttribute stores objects server-side in a flash map that survives exactly one redirect, so they aren't in the URL and can be complex objects.

open as a page

What is the difference between Model, ModelMap, and ModelAndView, and how do controller model attributes reach the view?

level: juniorimportance: should knowfreq 55%

basics

~20 s

Model is an interface for adding named attributes to the view. ModelMap is a Map-based implementation of that idea. ModelAndView bundles the model AND the view name/object together as a return value. Attributes you add become variables the view template can render.

open as a page

How do you implement and register a custom HandlerMethodArgumentResolver, and where does it sit relative to the built-in resolvers?

level: seniorimportance: should knowfreq 45%

basics

~10 s

Implement HandlerMethodArgumentResolver with supportsParameter (usually keyed on a custom annotation or type) and resolveArgument (pull the value from the request). Register it by overriding WebMvcConfigurer.addArgumentResolvers. It runs after the built-in resolvers.

open as a page

What is HandlerMethodReturnValueHandler, and how does Spring decide what to do with whatever a controller method returns?

level: seniorimportance: should knowfreq 40%

basics

~20 s

It's the return-side strategy interface. After the method runs, Spring finds the first HandlerMethodReturnValueHandler whose supportsReturnType is true and calls handleReturnValue, which turns the return value into a view, a serialized body, a redirect, etc.

open as a page

A team wants a custom resolver to intercept a parameter that Spring currently data-binds as a @ModelAttribute. Why does addArgumentResolvers work here but not for overriding @RequestParam, and how would you fully control ordering?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Custom resolvers are inserted after the specific built-ins but before the two catch-all resolvers (simple-type request param and default model-attribute). So a custom resolver beats the model-attribute catch-all, but never beats the explicit @RequestParam resolver. For total control, set the full ordered list on RequestMappingHandlerAdapter.

open as a page