Bean Validation
IntermediateJakarta Bean Validation (@Valid, @NotBlank, @Size, @Email) validates request bodies before they reach your service. Custom validators handle business rules that built-in constraints can't express.
Overview
Spring Boot auto-configures Bean Validation (Hibernate Validator) when spring-boot-starter-validation is on the classpath. Adding @Valid to a @RequestBody parameter triggers constraint checking before the method runs — violations throw MethodArgumentNotValidException, which Spring maps to a 400 response with field-level error details. For complex cross-field validation, implement ConstraintValidator with a custom annotation.
Built-in Constraints
Jakarta Validation ships with constraints for most common cases. Apply them to DTO fields — they work on both class fields and Java record components. Nest @Valid on fields to validate inner objects.
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
// Request DTO with constraints
public record CreateUserRequest(
@NotBlank(message = "Email is required")
@Email(message = "Invalid email format")
String email,
@NotBlank
@Size(min = 8, max = 100, message = "Password must be 8-100 characters")
String password,
@NotBlank
@Size(min = 2, max = 100)
String fullName,
@Min(value = 13, message = "Must be at least 13")
@Max(value = 120)
Integer age,
@Pattern(regexp = "^[6-9]\d{9}$", message = "Invalid Indian mobile number")
String mobile,
@Valid // triggers nested validation
@NotNull
AddressDto address
) {}
public record AddressDto(
@NotBlank String street,
@NotBlank String city,
@NotBlank @Size(min = 6, max = 6) String pincode
) {}
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public UserDto createUser(@RequestBody @Valid CreateUserRequest req) {
// Only reached if ALL constraints pass
return userService.create(req);
}
}Custom Validator and Error Handling
When built-in constraints are insufficient, implement ConstraintValidator<A, T>. Returning consistent error responses from MethodArgumentNotValidException makes your API easier to consume.
// Custom annotation
@Documented
@Constraint(validatedBy = UniqueEmailValidator.class)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UniqueEmail {
String message() default "Email already registered";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
// Validator implementation
@Component
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {
private final UserRepository userRepository;
public UniqueEmailValidator(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public boolean isValid(String email, ConstraintValidatorContext ctx) {
if (email == null) return true; // let @NotBlank handle nulls
return !userRepository.existsByEmail(email);
}
}
// Use on DTO
public record CreateUserRequest(
@UniqueEmail @Email @NotBlank String email,
// ...
) {}
// Global error handler — consistent 400 response
@RestControllerAdvice
public class ValidationExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, Object> handleValidation(MethodArgumentNotValidException ex) {
Map<String, String> fieldErrors = new LinkedHashMap<>();
ex.getBindingResult().getFieldErrors()
.forEach(e -> fieldErrors.put(e.getField(), e.getDefaultMessage()));
return Map.of(
"status", 400,
"error", "Validation Failed",
"errors", fieldErrors
);
}
}Key Points to Remember
- 1Add spring-boot-starter-validation — Bean Validation is not included in spring-boot-starter-web.
- 2@Valid on @RequestBody triggers constraint checking; violations throw MethodArgumentNotValidException → 400.
- 3Use @Valid on nested fields to recursively validate inner DTOs.
- 4Implement ConstraintValidator for business-rule validation (unique email, valid coupon code, etc.).
- 5Handle MethodArgumentNotValidException in @ControllerAdvice to return consistent, field-level error JSON.
- 6@Validated at class level on @Service enables constraint validation on service method parameters too.
Interview Questions
Sign in to ask AriaWhat dependency do you need for Bean Validation in Spring Boot?
What exception is thrown when @Valid fails on a @RequestBody and what HTTP status does Spring return?
How do you write a custom Bean Validation constraint?
What is the difference between @Valid and @Validated?
How would you validate that two fields in a DTO have matching values (e.g. password confirm)?
Ask Aria about Bean Validation
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.