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
@NotNullโŒโœ…โœ…Any object type
@NotEmptyโŒโŒโœ…Collections, Strings
@NotBlankโŒโŒโŒStrings 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