Looking into Spring Validation

I recently had a look into Spring Validation and discovered new ways it can be used. Here's a simple example that is commonly used in Spring Boot applications.

A simple Person class with validation constraint annotations:

package com.example.spring_validated.lib;

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;

public class Person {

    @NotBlank
    private String name;

    @Min(18)
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

And a controller that uses the Person class as a dependency:

package com.example.spring_validated;

import com.example.spring_validated.lib.Person;
import jakarta.validation.Valid;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Validated
public class PersonController {

    @PostMapping("/person")
    public String createPerson(@Valid @RequestBody Person person) {
        return "Person created: " + person.getName();
    }
}

If we use this endpoint without giving a name in the request body, we will get a 400 Bad Request response since it does not meet the constraints in the Person class.

In my application, the Person class is declared in a separate library, meaning that Spring Validation actually works across different libraries. Additionally, not only will Spring Validation apply constraints from your dependency class, but it also works with sub-dependencies as long as they are annotated with @Valid, like this Address record:

package com.example.spring_validated.anotherlib;

import jakarta.validation.constraints.NotBlank;

public record Address(@NotBlank String street) {}
package com.example.spring_validated.lib;

import com.example.spring_validated.anotherlib.Address;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;

public class Person {

    @NotBlank
    private String name;

    @Min(18)
    private int age;

    @NotNull
    @Valid
    private Address address;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

My investigation was led by a desire to validate outside a controller, and I found that @Validated can also be applied to other beans like Services.

package com.example.spring_validated.app;

import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import com.example.spring_validated.lib.Person;
import jakarta.validation.Valid;

@Service
@Validated
public class PersonService {

  public void validate(@Valid Person person) {
    System.out.println("Validated person: " + person);
  }
}

This is useful in my case where I want to validate data classes that we formed via extensive business logic before it is persisted or sent to another service.

I found some new ways to apply validations in my applications and I hope you can too.