0
0
Spring Bootframework~7 mins

Custom validator annotation in Spring Boot

Choose your learning style9 modes available
Introduction

Custom validator annotations let you check if data meets your own rules. This helps keep your app data clean and correct.

You want to check a user input field with rules not covered by built-in validators.
You need to validate a complex object property with special logic.
You want to reuse the same validation rule on many fields or classes.
You want to keep validation logic separate from your main code for clarity.
Syntax
Spring Boot
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;

@Documented
@Constraint(validatedBy = YourValidatorClass.class)
@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface YourAnnotation {
    String message() default "Validation failed message";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

The @Constraint annotation links your custom annotation to its validator class.

The message is the error shown if validation fails.

Examples
This defines a custom annotation @NotEmptyString to check if a string is not empty.
Spring Boot
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;

@Documented
@Constraint(validatedBy = NotEmptyStringValidator.class)
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface NotEmptyString {
    String message() default "String must not be empty";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
This is the validator class that checks if the string is not null or empty.
Spring Boot
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class NotEmptyStringValidator implements ConstraintValidator<NotEmptyString, String> {
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return value != null && !value.trim().isEmpty();
    }
}
Sample Program

This example creates a custom annotation @StartsWithA that checks if a string starts with 'A'. The Person class uses it on the name field. The main method validates two persons and prints violations.

Spring Boot
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import jakarta.validation.constraints.NotNull;
import java.lang.annotation.*;
import java.util.Set;
import jakarta.validation.ConstraintViolation;

@Documented
@Constraint(validatedBy = StartsWithAValidator.class)
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@interface StartsWithA {
    String message() default "Must start with letter A";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

class StartsWithAValidator implements ConstraintValidator<StartsWithA, String> {
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return value != null && value.startsWith("A");
    }
}

class Person {
    @NotNull
    @StartsWithA
    String name;

    Person(String name) {
        this.name = name;
    }
}

public class CustomValidatorDemo {
    public static void main(String[] args) {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();

        Person p1 = new Person("Alice");
        Person p2 = new Person("Bob");

        Set<ConstraintViolation<Person>> violations1 = validator.validate(p1);
        Set<ConstraintViolation<Person>> violations2 = validator.validate(p2);

        System.out.println("Violations for p1: " + violations1.size());
        System.out.println("Violations for p2: " + violations2.size());
        for (var v : violations2) {
            System.out.println(v.getPropertyPath() + ": " + v.getMessage());
        }
    }
}
OutputSuccess
Important Notes

Always implement ConstraintValidator with your annotation and the type you want to validate.

Use @Target to specify where your annotation can be used (fields, methods, etc.).

Remember to add @Retention(RetentionPolicy.RUNTIME) so the annotation is available at runtime.

Summary

Custom validator annotations let you create your own rules for data checks.

You link the annotation to a validator class that contains the logic.

Use them to keep validation clean and reusable across your app.