skip to content

Besides @ExceptionHandler, what do @InitBinder and @ModelAttribute methods do inside a @ControllerAdvice?

level: middleimportance: nice to knowfreq 32%

answer

  1. @InitBinder -> WebDataBinder, returns void
  2. setDisallowedFields = anti mass-assignment
  3. @ModelAttribute runs before every handler
  4. does NOT touch @RequestBody JSON
  5. three method roles are mutually exclusive

basics

~20 s

In a @ControllerAdvice, @InitBinder methods customize how request data is bound (e.g. register a date format or block certain fields) for many controllers, and @ModelAttribute methods add shared values to the model before every handler runs.

solid answer

~40 s

A @ControllerAdvice can hold three method types, not just @ExceptionHandler. An @InitBinder method receives a WebDataBinder and customizes binding globally — registering custom PropertyEditors/Formatters (e.g. a date pattern), calling setDisallowedFields to block mass-assignment, or adding a Validator. A @ModelAttribute method runs before every in-scope controller handler and adds a shared attribute to the Model (e.g. current user, reference data for views). Both apply to whatever controllers the advice is scoped to, so they are a way to share binding rules or model data across many controllers without duplication. Key caveats: @InitBinder methods must return void and cannot also be @ModelAttribute; global @ModelAttribute methods run on every request in scope, so keep them cheap; and for REST/JSON APIs @ModelAttribute is rarely useful (it targets the model, mostly for views).

code

java · 18 lines
java
@ControllerAdvice
public class BindingAdvice {

    // Applies date parsing + blocks mass-assignment across all in-scope controllers
    @InitBinder
    public void configureBinder(WebDataBinder binder) {
        var df = new SimpleDateFormat("yyyy-MM-dd");
        df.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(df, true));
        binder.setDisallowedFields("id", "role", "enabled"); // over-posting guard
    }

    // Shared model data for server-rendered views
    @ModelAttribute("appVersion")
    public String appVersion() {
        return "2.4.0";
    }
}

go deeper

for a junior

Know @InitBinder customizes binding and @ModelAttribute adds shared model data.

for a middle

Explain setDisallowedFields for over-posting and that global versions apply across controllers.

for a senior

Point out @InitBinder does not cover @RequestBody JSON and prefer DTOs + validation.

for a principal

Weigh centralized binding advice vs explicit DTO boundaries for maintainability and security.

## The three roles of @ControllerAdvice `@ControllerAdvice` centralizes any of: `@ExceptionHandler`, `@InitBinder`, and `@ModelAttribute` methods so they apply across many controllers. This question is about the latter two. ## @InitBinder (global data-binding customization) When Spring MVC binds request parameters / form fields / path variables to a handler method's argument object, it uses a `WebDataBinder`. An `@InitBinder` method receives that binder and lets you configure the binding **before** it happens: - **Register converters/editors**: `binder.registerCustomEditor(LocalDate.class, new CustomDateEditor(...))` or add `Formatter`s so a String param is parsed to your type. - **Restrict binding for security**: `binder.setDisallowedFields("id", "role")` or `setAllowedFields(...)` to prevent **mass assignment / over-posting** (a client sneaking `role=ADMIN` into a form bound to an entity). This is the most interview-relevant use. - **Add a Validator**: `binder.addValidators(new MyValidator())`. Rules: an `@InitBinder` method **returns void**; it can accept `WebDataBinder`, `WebRequest`, `Locale`, etc. You can narrow it to specific attribute names via `@InitBinder("user")`. In a `@ControllerAdvice` it applies to all in-scope controllers; declared inside a single controller it applies only there. ## @ModelAttribute (shared model data) A method annotated `@ModelAttribute` (that returns a value, or takes a `Model`) runs **before every** handler method in scope, populating the `Model` with a shared attribute — e.g. the authenticated user, dropdown/reference data, feature flags — so server-rendered views can use it without each handler recomputing it. In a `@ControllerAdvice` it becomes an app-wide (or scoped) model contributor. ## Interaction / precedence Both global (advice-level) and local (controller-level) `@InitBinder`/`@ModelAttribute` methods exist; local ones run in addition to global ones for that controller. `@ExceptionHandler`, `@InitBinder`, and `@ModelAttribute` are mutually exclusive on a single method. ## Gotchas / when NOT to use - For **REST/JSON** APIs, `@ModelAttribute` is usually irrelevant (there is no view/model rendering). `@RequestBody` deserialization is done by `HttpMessageConverter`s, not the `WebDataBinder`, so `@InitBinder` field rules do **not** apply to JSON bodies — they apply to query/form/path binding. Enforce JSON constraints via DTO design + Bean Validation instead. - A global `@ModelAttribute` runs on **every** request in scope; expensive work there hurts all endpoints. - `setDisallowedFields` uses field-name patterns and is easy to get wrong; prefer explicit DTOs over binding straight to entities. ## When to use Use `@InitBinder` in advice to share a date/enum format or a security field allow-list across a group of form-based controllers; use `@ModelAttribute` in advice to inject common page data for a set of server-rendered pages.

  • Does an @InitBinder setDisallowedFields rule protect a @RequestBody JSON endpoint from over-posting?
    No. @RequestBody is deserialized by HttpMessageConverters (Jackson), not the WebDataBinder, so @InitBinder field rules don't apply. Protect JSON APIs with narrow DTOs and Bean Validation, not @InitBinder.
  • Can one method be both @ModelAttribute and @ExceptionHandler?
    No — the three roles (@ExceptionHandler, @InitBinder, @ModelAttribute) are mutually exclusive on a single method; each does a distinct thing in the request lifecycle.

saying these in an interview costs you the question

  • Claiming @InitBinder field allow/deny lists secure @RequestBody JSON endpoints
  • Putting expensive logic in a global @ModelAttribute that runs on every request
  • Thinking @InitBinder methods can return a value
  • Binding directly to JPA entities and relying on setDisallowedFields instead of DTOs

context