How to Create Custom Validator in Spring Boot Easily
To create a custom validator in Spring Boot, define a new annotation interface with
@Constraint and implement ConstraintValidator for validation logic. Then, apply this annotation on your model fields to enforce custom validation rules.Syntax
Creating a custom validator involves two main parts:
- Annotation Interface: Defines the custom annotation with
@Constraintand metadata. - Validator Class: Implements
ConstraintValidatorto hold the validation logic.
Use the annotation on fields you want to validate.
java
import jakarta.validation.Constraint; import jakarta.validation.Payload; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Constraint(validatedBy = YourValidator.class) @Target({ ElementType.FIELD, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface YourConstraint { String message() default "Invalid value"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; public class YourValidator implements ConstraintValidator<YourConstraint, String> { @Override public boolean isValid(String value, ConstraintValidatorContext context) { // validation logic here return true; // or false } }
Example
This example creates a custom validator @StartsWithA that checks if a string starts with the letter 'A'. It shows how to define the annotation, implement the validator, and use it on a model field.
java
import jakarta.validation.Constraint; import jakarta.validation.Payload; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Constraint(validatedBy = StartsWithAValidator.class) @Target({ ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) public @interface StartsWithA { String message() default "Must start with letter A"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; public class StartsWithAValidator implements ConstraintValidator<StartsWithA, String> { @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (value == null) return true; // null handled by @NotNull if needed return value.startsWith("A"); } } // Model class using the custom validator import jakarta.validation.constraints.NotNull; public class User { @NotNull @StartsWithA private String name; public User(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } // Example usage in a Spring Boot service or controller import jakarta.validation.Validation; import jakarta.validation.Validator; import jakarta.validation.ValidatorFactory; import jakarta.validation.ConstraintViolation; import java.util.Set; public class ValidatorTest { public static void main(String[] args) { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); Validator validator = factory.getValidator(); User user1 = new User("Alice"); User user2 = new User("Bob"); Set<ConstraintViolation<User>> violations1 = validator.validate(user1); Set<ConstraintViolation<User>> violations2 = validator.validate(user2); System.out.println("User1 violations: " + violations1.size()); System.out.println("User2 violations: " + violations2.size()); violations2.forEach(v -> System.out.println(v.getMessage())); } }
Output
User1 violations: 0
User2 violations: 1
Must start with letter A
Common Pitfalls
- Forgetting to add
@Constraint(validatedBy = ...)on the annotation interface causes Spring to ignore the validator. - Not implementing
ConstraintValidatorwith correct generic types leads to runtime errors. - Returning
truefornullvalues inisValidis standard; use@NotNullseparately if nulls are invalid. - Not registering the validator if using custom validation outside Spring Boot's automatic scanning.
java
/* Wrong: Missing @Constraint annotation */ public @interface MissingConstraint { String message() default "Error"; } /* Right: Include @Constraint with validator class */ import jakarta.validation.Constraint; import jakarta.validation.Payload; import java.lang.annotation.Target; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Constraint(validatedBy = CorrectValidator.class) @Target({ ElementType.FIELD, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface CorrectConstraint { String message() default "Error"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
Quick Reference
Summary tips for creating custom validators in Spring Boot:
- Define annotation with
@Constraint,@Target, and@Retention. - Implement
ConstraintValidatorwith proper types. - Return
truefornullvalues inisValidunless@NotNullis used. - Use the annotation on fields to enforce validation.
- Test validation with
Validatorfromjakarta.validation.
Key Takeaways
Create a custom annotation with @Constraint and link it to a validator class.
Implement ConstraintValidator with validation logic in isValid method.
Use @NotNull separately to handle null checks, as isValid should return true for null.
Apply your custom annotation on model fields to enforce validation rules.
Test your validator using jakarta.validation.Validator to catch errors early.