What does @Valid on a Spring MVC controller parameter do, and what happens when validation fails?
answer
- @Valid triggers Hibernate Validator
- no BindingResult -> exception -> 400
- BindingResult must be right after the arg
- RequestBody -> MethodArgumentNotValidException
- ModelAttribute -> BindException
basics
~20 s@Valid tells Spring to run Jakarta Bean Validation constraints (like @NotNull, @Size) on the argument. If a constraint fails and there is no BindingResult next to it, Spring throws an exception that becomes an HTTP 400 Bad Request.
solid answer
~40 s@Valid (jakarta.validation.Valid) marks a controller argument for validation. When Spring binds a @RequestBody or @ModelAttribute, it invokes the JSR-380 Bean Validation provider (Hibernate Validator by default) to check the field constraints (@NotNull, @Size, @Email, @Min, etc.) on that object. If any constraint is violated and there is no BindingResult/Errors parameter immediately after the validated argument, Spring raises an exception — MethodArgumentNotValidException for @RequestBody, or BindException for @ModelAttribute form binding — which the default handler turns into a 400 Bad Request. If you add a BindingResult right after the @Valid argument, Spring instead injects the errors and lets your method decide what to do, so no exception is thrown.
code
java · 17 linespublic record CreateUser(
@NotBlank String name,
@Email String email,
@Min(18) int age) {}
@RestController
@RequestMapping("/users")
class UserController {
// No BindingResult -> invalid body throws
// MethodArgumentNotValidException -> 400 by default.
@PostMapping
ResponseEntity<Void> create(@Valid @RequestBody CreateUser body) {
// reached only if body is valid
return ResponseEntity.status(201).build();
}
}go deeper
Know that @Valid runs the constraint annotations and a failure yields a 400.
Explain the exception types (MethodArgumentNotValidException vs BindException) and the BindingResult opt-out.
Discuss handling the exception centrally in @RestControllerAdvice vs in-method BindingResult, and the ordering gotcha.
Reason about API error-contract design: consistent error envelopes, when to surface field errors vs generic messages, and provider configuration.
## What @Valid is `@Valid` comes from **Jakarta Bean Validation** (the spec formerly JSR-303/JSR-380), package `jakarta.validation.Valid` (older apps: `javax.validation.Valid`). Bean Validation is a standard for declaring **constraints** on object fields with annotations such as `@NotNull`, `@NotBlank`, `@Size`, `@Min`, `@Max`, `@Email`, `@Pattern`. A **validation provider** actually enforces them; the default in Spring Boot is **Hibernate Validator** (pulled in by `spring-boot-starter-validation`). ## What Spring MVC does with it When a controller method parameter is annotated with `@Valid` (or Spring's `@Validated`), Spring MVC runs the validator **after data binding** and **before your method body executes**: - For a **`@RequestBody`** parameter, the JSON is deserialized by Jackson into the object, then validated. - For a **`@ModelAttribute`** parameter (HTML form / query params), request parameters are bound onto the object, then validated. ## What happens on failure The outcome depends on whether you declare a `BindingResult`/`Errors` parameter **immediately after** the validated argument: 1. **No `BindingResult` present** → Spring throws: - `MethodArgumentNotValidException` for a `@RequestBody` argument, or - `BindException` for a `@ModelAttribute` argument. Both are handled by Spring's default exception resolver and produce **HTTP 400 Bad Request**. 2. **`BindingResult` present** → Spring does **not** throw. It populates the `BindingResult` with the errors and calls your method, so you can inspect `bindingResult.hasErrors()` and respond however you like (e.g., re-render a form). ## Key gotcha: order matters The `BindingResult` must come **directly after** the object it applies to. This is wrong: ```java public String submit(@Valid Order order, Model model, BindingResult result) // BROKEN ``` Here Spring can't associate `result` with `order`, so it throws instead of populating errors, and you may get a confusing exception. ## @Valid vs @Validated `@Valid` is the standard annotation. `@Validated` (`org.springframework.validation.annotation.Validated`) is Spring's variant that additionally supports **validation groups**. Both trigger validation on method arguments; use `@Validated` when you need groups. ## When to use which style - **REST APIs**: prefer letting `MethodArgumentNotValidException` propagate and handle it in a `@RestControllerAdvice` `@ExceptionHandler` to produce a consistent error body. Don't add `BindingResult` unless you truly need custom in-method logic. - **Server-rendered forms**: add `BindingResult` so you can re-display the form with field errors instead of a 400 page.
- Why does adding a BindingResult parameter change the behavior?Its presence tells Spring you intend to handle errors yourself, so instead of throwing MethodArgumentNotValidException/BindException, Spring populates the BindingResult and calls your method. You then check hasErrors().
- Which dependency provides the validator in Spring Boot?spring-boot-starter-validation, which brings in Hibernate Validator (the reference implementation of Jakarta Bean Validation). Without it, constraint annotations are ignored.
saying these in an interview costs you the question
- Thinking @Valid returns a boolean or that you must call it manually
- Believing validation still works after removing spring-boot-starter-validation
- Putting BindingResult anywhere except immediately after the validated argument
- Assuming @Valid validates path variables or simple @RequestParam values (that needs @Validated on the class + method-level validation)