21. Bean Validation & API Contract Enforcement

Bean Validation Annotations & @Valid Trigger

Master Spring Boot Bean Validation, constraint annotations (@NotBlank, @NotNull, @Email), the @Valid validation trigger, and skip-service rules.

Bean Validation Annotations & @Valid Trigger

While DTOs specify which fields can cross API boundaries, Bean Validation enforces what values are legally acceptable before executing business logic.


1. Adding Spring Boot Validation Starter

Spring Boot validation support requires spring-boot-starter-validation in pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2. @NotNull vs @NotBlank vs @NotEmpty

Choosing the correct constraint annotation prevents common edge-case validation bugs:

public class StudentRequestDto {

    // 1. @NotBlank: Rejects null, "", and whitespace "   "
    @NotBlank(message = "Name is mandatory and cannot be blank")
    @Size(min = 2, max = 50, message = "Name must be 2 to 50 characters")
    private String name;

    // 2. @Email: Enforces valid email pattern
    @NotBlank(message = "Email is mandatory")
    @Email(message = "Email must be a valid email format")
    private String email;

    // 3. @NotNull: Rejects null (Used for numbers/dates where String logic does not apply)
    @NotNull(message = "Age is mandatory")
    @Min(value = 18, message = "Age must be at least 18")
    @Max(value = 60, message = "Age must not exceed 60")
    private Integer age;
}

Constraint Comparison Matrix

AnnotationRejects null?Rejects "" (Empty)?Rejects " " (Whitespace)?Target Data Type
@NotNull✅ Yes❌ No❌ NoNumbers, Dates, Objects
@NotEmpty✅ Yes✅ Yes❌ NoStrings, Collections, Arrays
@NotBlank✅ Yes✅ Yes✅ YesString text fields only

3. @Valid: Triggering Validation at the Controller Boundary

Placing constraint annotations on DTO fields does NOT automatically execute validation. The Controller MUST declare @Valid on the @RequestBody parameter:

@RestController
@RequestMapping("/api/students")
public class StudentController {

    private final StudentService service;

    public StudentController(StudentService service) {
        this.service = service;
    }

    // @Valid triggers Spring to evaluate DTO constraints before invoking createStudent
    @PostMapping
    public StudentResponseDto createStudent(@Valid @RequestBody StudentRequestDto requestDto) {
        return service.createStudent(requestDto);
    }
}
Constraint Annotations (@NotBlank, @Min) ──► Rule Book (Defines validation constraints)
@Valid Annotation                        ──► Security Guard (Triggers execution of rules)

4. Internal Validation Execution Pipeline

alt [Validation Failed (Invalid Payload)] [Validation Passed (Valid Payload)] POST /api/students (JSON Payload) Jackson Deserializes JSON ──► StudentRequestDto @Valid Trigger: Evaluate DTO Constraints ConstraintViolations (e.g. age = 15 < 18) HTTP 400 Bad Request (Service NOT Called!) All Constraints Passed Call studentService.createStudent(dto) HTTP 200/201 Success Response HTTP Client Spring MVC Dispatcher Bean Validator StudentService Layer

Crucial Guarantee: If validation fails, Spring MVC aborts execution at the Controller boundary and returns an HTTP 400 Bad Request. The Service Layer is never invoked with invalid data!


❓ Knowledge Check

Knowledge Check

What is the difference between @NotBlank and @NotNull in Bean Validation?

Knowledge Check

What happens if a client submits an invalid request payload to a Controller method annotated with @Valid @RequestBody?

On this page