Declarative Validation with Bean Validation
Spring Boot integrates with the Jakarta Bean Validation specification (implemented by Hibernate Validator) to let you declare validation rules directly on DTO fields using annotations like @NotNull, @NotBlank, @Size, @Email, @Min, and @Max. This keeps validation rules close to the data they constrain and eliminates repetitive manual if-checks scattered through service methods — the constraints become part of the class definition itself, self-documenting the expected shape of valid input.
Cricket analogy: It's like a bowler's run-up being checked automatically against a fixed no-ball line rather than an umpire mentally judging foot placement each time — declarative constraints enforce the rule consistently without manual checks.
Triggering Validation with @Valid
Annotations on a DTO's fields are only inert metadata until something triggers validation against them. Adding @Valid (or Spring's @Validated for group-based validation) before a @RequestBody parameter tells Spring's argument resolver to run the DTO through the configured Validator before the controller method body even executes; if any constraint fails, Spring throws a MethodArgumentNotValidException and the handler method never runs, short-circuiting invalid requests before they can touch business logic.
Cricket analogy: Like a stadium's turnstile that checks your ticket's barcode before letting you through the gate — @Valid checks the request before it ever reaches the 'match' (handler method).
public record CreateUserRequest(
@NotBlank(message = "Name is required") String name,
@NotBlank
@Email(message = "Must be a valid email address")
String email,
@Min(value = 18, message = "Must be at least 18 years old")
int age) {}
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
public ResponseEntity<UserDto> createUser(
@RequestBody @Valid CreateUserRequest request) {
UserDto user = userService.create(request);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
}Custom Validators for Complex Rules
Built-in annotations cover common single-field constraints, but real applications often need rules that span multiple fields or require a database lookup, such as 'password and confirmPassword must match' or 'email must not already be registered.' For these, you implement ConstraintValidator<YourAnnotation, TargetType>, define a custom annotation with @Constraint(validatedBy = ...), and apply it either on the class (for cross-field checks) or on a single field (for lookups via an injected repository or service).
Cricket analogy: Like a DRS review that goes beyond simple line-calls (built-in checks) to a nuanced umpire's-call decision requiring ball-tracking analysis (custom validator logic) for edge cases.
Use validation groups (via @Validated(OnCreate.class)) when the same DTO needs different rules for create vs update operations — for example, requiring a password on creation but making it optional on update.
@Valid on a nested object field only cascades validation into that nested object if the field itself is also annotated with @Valid. Forgetting this is a common bug: the outer DTO validates fine while an invalid nested object silently passes through.
- Bean Validation annotations (@NotBlank, @Size, @Email, @Min/@Max) declare constraints directly on DTO fields.
- @Valid on a @RequestBody parameter triggers validation before the controller method body executes.
- A failed validation throws MethodArgumentNotValidException, which should be handled centrally.
- Custom validators implement ConstraintValidator for cross-field or lookup-based rules.
- Validation groups allow the same DTO to apply different rules for different operations (create vs update).
- Nested objects require an explicit @Valid on the field to cascade validation into them.
- Keeping validation declarative on the DTO improves readability and removes manual if-check clutter from services.
Practice what you learned
1. What triggers Bean Validation constraints to actually run against a @RequestBody parameter?
2. What exception is thrown when @Valid validation fails on a request body?
3. When should you write a custom ConstraintValidator instead of using a built-in annotation?
4. What happens if a nested object field is not explicitly annotated with @Valid inside a parent DTO?
5. What is the purpose of validation groups like @Validated(OnCreate.class)?
Was this page helpful?
You May Also Like
Exception Handling in Spring Boot
Learn how to handle errors gracefully in Spring Boot REST APIs using @ExceptionHandler, @ControllerAdvice, and structured error responses.
Request and Response Bodies
Learn how Spring Boot serializes and deserializes JSON request and response bodies using @RequestBody, @ResponseBody, and Jackson.
Building REST Controllers
Learn how to build REST controllers in Spring Boot using @RestController, structure methods cleanly, and return well-formed HTTP responses.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics