Overview

Bean Validation is a Java standard (JSR 380) that provides a consistent way to perform validation on Java Bean objects.

  • primarily uses annotations to define constraints and supports validation at the field, method parameter, constructor parameter, and class levels
  • Spring fully integrates Bean Validation and offers the following features:
    • Automatic validation of controller method parameters
    • Declarative validation support via @Valid and @Validated annotations
    • Exception handling mechanisms for validation failures
    • Support for custom validator registration and extension
  • Bean VS Bean validation
    • Bean - spring container가 관리하는 객체
    • JavaBeans
      • The Jakarta Bean Validation specification (which gives us annotations like @NotNull and @Valid) was named this way because it was designed to validate the data held within these simple JavaBeans
  • Tips
    • Validation annotations can be applied to fields, parameters, return values, and more.
    • The @Validated annotation is more flexible than @Valid, allowing the use of validation groups.
    • You can create custom annotations to abstract complex business rules into reusable validators.
    • In production, separate validation failure messages between logging and user feedback for better management.
  • Input validation locations (service, controller)
  • Custom Validator

build.gradle

implementation 'org.springframework.boot:spring-boot-starter-validation'
  • transitively includes these dependencies (or compatible versions):
    • org.hibernate.validator:hibernate-validator
    • org.glassfish:jakarta.el

Why

  • User may make mistake/intentionally submit bad data
  • If unchecked this can led to these problems
    • Compromised Database Integrity: Storing incorrect or inconsistent data.
    • System Exceptions: Causing the application to crash or behave unpredictably.
    • Business Logic Errors: Leading to incorrect calculations or process flows.
    • Security Threats: Opening vulnerabilities like SQL Injection, Cross-Site Scripting (XSS), etc.
  • 4 Potential problems in a Spring application - Invalid User Input

Main annotations

NEED TO KNOW EVERYTHING LOL

CategoryAnnotationsDescription
String@NotNull, @NotEmpty, @NotBlankValidate null, empty, or blank strings
String@Size, @LengthValidate string length
String@Email, @PatternFormat validation (email, regex)
Number@Min, @Max, @RangeNumeric range constraints
Number@Positive, @NegativeSign validation (positive/negative)
Number@Digits, @DecimalMin, @DecimalMaxPrecision and decimal validation
Date@Past, @Future, etc.Time-based validation
Collection@Size, @Valid, element-level annotationsValidate lists/maps and their elements
  • @Pattern
    • Regex is still widely used
    • even tho GPT is good at it, it will still be beneficial (and won’t hurt you) if you get used to it

@NotNull, @NotEmpty, @NotBlank

Annotationnull"" (empty)" " (whitespace)Best For
@NotNullAny object type
@NotEmptyCollections, Strings
@NotBlankStrings only

Before and after bean validation

Before (Not using)

public class User {
    private String username;
    private String email;
    private int age;
 
    public User(String username, String email, int age) {
        this.username = username;
        this.email = email;
        this.age = age;
    }
 
    // 게터 생략
}
public class Validator {
    public static void validateUser(User user) {
        if (user.getUsername() == null || user.getUsername().isBlank()) {
            throw new IllegalArgumentException("사용자 이름은 필수입니다.");
        }
        if (!user.getEmail().contains("@")) {
            throw new IllegalArgumentException("이메일 형식이 올바르지 않습니다.");
        }
        if (user.getAge() < 18) {
            throw new IllegalArgumentException("나이는 18세 이상이어야 합니다.");
        }
    }
}

After (Using)

import jakarta.validation.constraints.*;
 
public class User {
 
    @NotBlank(message = "사용자 이름은 필수입니다.")
    private String username;
 
    @Email(message = "이메일 형식이 올바르지 않습니다.")
    private String email;
 
    @Min(value = 18, message = "나이는 18세 이상이어야 합니다.")
    private int age;
 
    public User(String username, String email, int age) {
        this.username = username;
        this.email = email;
        this.age = age;
    }
 
    // 게터 생략
    public String getUsername() { return username; }
    public String getEmail() { return email; }
    public int getAge() { return age; }
}

Validation in Client & Server side

Validation should happen in two key places, with each having a distinct role.

LocationPrimary RoleExamples
Client-Side (The Browser)Improve User Experience (UX) by providing instant feedback.Using HTML5 attributes like required or type="email", and performing checks with JavaScript before submitting a form
Server-Side (The Application)Act as the final authority to guarantee data integrity and enforce security. Client-side validation can be bypassed, so server-side validation is mandatory.@Valid and @Validated annotations
• Flexibly handling validation errors using the BindingResult object
• Validating complex business rules separately within the service layer

Some other resources