What is the difference between Model, ModelMap, and ModelAndView, and how do controller model attributes reach the view?
answer
- Model = interface, ModelMap = Map impl, ModelAndView = return object
- BindingAwareModelMap is what's injected
- attributes -> ModelAndViewContainer -> request attributes
- addAttribute(obj) auto-names by type
- Model is view-layer, not for REST
basics
~20 sModel 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.
solid answer
~40 s`Model` is the interface controllers usually declare as a parameter to expose attributes to the view (`model.addAttribute("user", u)`). `ModelMap` is a `LinkedHashMap`-based class implementing the same fluent add-attribute API; the concrete object Spring injects is `BindingAwareModelMap` (an `ExtendedModelMap`), which also implements `Model`. `ModelAndView` is different: it's a return-value object that packages both the model map and the view (a name resolved by a `ViewResolver`, or a `View` instance). Attributes travel in the `ModelAndViewContainer`; after the method returns, the `DispatcherServlet` exposes them to the chosen `View` for rendering (e.g. as request attributes for JSP/Thymeleaf). Prefer a `Model` parameter for readability; use `ModelAndView` when you want to set model and view in one returned object.
code
java · 18 lines@Controller
class OrderController {
// Style A: Model parameter (most common)
@GetMapping("/orders/{id}")
String show(@PathVariable Long id, Model model) {
model.addAttribute("order", service.find(id)); // named explicitly
return "orders/show"; // logical view name -> ViewResolver
}
// Style B: ModelAndView return (model + view in one object)
@GetMapping("/orders")
ModelAndView list() {
ModelAndView mav = new ModelAndView("orders/list");
mav.addObject("orders", service.all()); // auto/explicit naming
return mav;
}
}go deeper
Distinguish the three and know addAttribute exposes data to the view.
Explain the shared BindingAwareModelMap, auto-naming, and the view-render copy to request attributes.
Tie in @ModelAttribute methods/parameters and ModelMethodProcessor/MapMethodProcessor resolution.
Advise on when Model is inappropriate (REST) and the redirect/flash boundary for model data.
### The three types - **`Model`** (`org.springframework.ui.Model`) — an interface with methods like `addAttribute(String, Object)`, `addAttribute(Object)` (auto-names by type), `addAllAttributes(...)`, `mergeAttributes(...)`, `containsAttribute(...)`. You declare it as a **method parameter**; Spring populates it and reads it back after invocation. - **`ModelMap`** (`org.springframework.ui.ModelMap`) — a concrete class extending `LinkedHashMap<String,Object>` that provides the same fluent `addAttribute` chaining. It is a `Map`, so it can be used where a map is expected. It does **not** implement `Model` by itself; the class that implements both is `ExtendedModelMap` (and its subclass `BindingAwareModelMap`, which is what Spring actually hands you). - **`ModelAndView`** (`org.springframework.web.servlet.ModelAndView`) — a **return type**, not a parameter. It holds a model map plus a *view*: either a logical **view name** (String, later run through the `ViewResolver` chain) or a concrete `View` object. Use it to return both in one object: `return new ModelAndView("orders", "order", order);`. ### How attributes flow to the view 1. Whatever you add (via a `Model`/`ModelMap` parameter, a returned `ModelAndView`, or an `@ModelAttribute`-annotated method) ends up in the **`ModelAndViewContainer`**. 2. When the handler returns a view name (or void with a `Model`), Spring resolves a `View` via `ViewResolver`s. 3. `View.render(model, request, response)` runs; for server-side templates the model entries are copied to **request attributes**, so a JSP/Thymeleaf/FreeMarker template can reference `${user}` etc. ### `@ModelAttribute` interactions - On a **method**: it runs before every handler in the controller and pre-populates the model (e.g. reference data for a form). - On a **parameter**: it binds request params onto an object and also adds it to the model under its attribute name — resolved by `ModelAttributeMethodProcessor`/`ServletModelAttributeMethodProcessor`. ### Which resolver injects a Model parameter `ModelMethodProcessor` supports `Model`; `MapMethodProcessor` supports raw `Map`/`ModelMap`. Both simply return the container's model, so all three parameter styles write to the *same* underlying map. ### Gotchas - The **injected object is mutable and shared** — all three parameter styles point at the same `BindingAwareModelMap`, so adding via `ModelMap` and via `Model` in the same method is additive, not conflicting. - `addAttribute(Object value)` **auto-generates the name** from the type (e.g. a `User` → `user`); a `null` or empty collection can throw or produce a surprising name, so prefer the explicit two-arg form. - Model attributes are **not** automatically added to a redirect URL — that's what `RedirectAttributes` is for. On a redirect, ordinary model attributes are appended as query parameters only if the redirect uses `RedirectView` expansion; flash attributes are the safe mechanism. - **`Model` is UI-layer**, tied to view rendering. For pure REST/`@ResponseBody` endpoints you typically don't use `Model` at all. ### When to use which - Declare a **`Model` parameter** for the common case (cleanest signature). - Return **`ModelAndView`** when branching logic must choose different views and set model data together. - Use **`ModelMap`** rarely — mainly when you need `Map` semantics or interop.
- You call model.addAttribute(myUser) without a name. What key is used?Spring derives the name from the type via Conventions.getVariableName — a User becomes "user", a List<User> becomes "userList". Prefer the explicit two-arg form to avoid surprises and to survive obfuscation.
saying these in an interview costs you the question
- Saying ModelAndView is a method parameter you inject
- Claiming ModelMap implements Model (it's ExtendedModelMap/BindingAwareModelMap that does)
- Thinking model attributes are automatically carried across a redirect