Custom Validation Annotations in Spring Boot – Complete Guide with Examples

Custom Validation Annotations in Spring Boot (Complete In-Depth Guide)

Spring Boot provides many built-in validation annotations like @NotNull, @Email, and @Size. But in real applications, these are often not enough. This is where custom validation annotations become extremely important.

This guide explains:
  • Why and when custom validation is needed
  • How custom validation works internally
  • Step-by-step creation of a custom annotation
  • Advanced examples used in real projects
  • Common mistakes and interview tips

1️⃣ Why Do We Need Custom Validation?

Built-in annotations cover only generic cases. Real-world APIs require business-specific validation.

Examples:

  • Email must belong to company domain
  • Password must contain uppercase, lowercase, number
  • User age must match role rules
  • Mobile number format depends on country
Interview insight: “Built-in validation is generic, custom validation handles business rules.”

2️⃣ How Custom Validation Works Internally

Spring Boot custom validation is based on Bean Validation. Each custom annotation is connected to a Validator class.

@CustomAnnotation
     ↓
ConstraintValidator
     ↓
isValid() method
     ↓
true → validation passed
false → validation failed

This mechanism integrates seamlessly with @Valid in REST APIs.

If you’re new to validation basics, read: Spring Boot REST API Validation (All Annotations)


3️⃣ Step-by-Step: Creating a Custom Validation Annotation

Step 1: Create the Custom Annotation

@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CompanyEmailValidator.class)
public @interface CompanyEmail {

  String message() default "Email must belong to company domain";

  Class<?>[] groups() default {};

  Class<? extends Payload>[] payload() default {};
}

Important parts:

  • @Constraint links annotation to validator
  • message is default error message
  • groups & payload are required by Bean Validation spec

Step 2: Create the Validator Class

public class CompanyEmailValidator
    implements ConstraintValidator<CompanyEmail, String> {

  @Override
  public boolean isValid(String value, ConstraintValidatorContext context) {
    if (value == null) return true; // let @NotNull handle null
    return value.endsWith("@company.com");
  }
}
Best practice: Return true for null and combine with @NotNull separately.

Step 3: Use Custom Annotation in DTO

public class UserRequest {

  @NotBlank
  private String name;

  @CompanyEmail
  private String email;
}

This works exactly like built-in annotations.


4️⃣ Using Custom Validation in REST Controller

@PostMapping("/users")
public String createUser(
    @Valid @RequestBody UserRequest request) {
  return "User created";
}

If validation fails, Spring throws an exception automatically.

Related: Spring Boot Annotations Explained


5️⃣ Advanced Example: Password Strength Validation

Custom Annotation

@Constraint(validatedBy = PasswordStrengthValidator.class)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface StrongPassword {

  String message() default "Password is too weak";

  Class<?>[] groups() default {};

  Class<? extends Payload>[] payload() default {};
}

Validator Implementation

public class PasswordStrengthValidator
    implements ConstraintValidator<StrongPassword, String> {

  @Override
  public boolean isValid(String password, ConstraintValidatorContext context) {
    if (password == null) return false;

    boolean hasUpper = password.matches(".*[A-Z].*");
    boolean hasLower = password.matches(".*[a-z].*");
    boolean hasDigit = password.matches(".*\\d.*");

    return password.length() >= 8 && hasUpper && hasLower && hasDigit;
  }
}
Interview gold: Custom validation keeps business rules out of controllers.

6️⃣ Class-Level Custom Validation (Cross-Field)

Sometimes validation depends on multiple fields. Example: password and confirmPassword must match.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PasswordMatchValidator.class)
public @interface PasswordMatch {
  String message() default "Passwords do not match";
  Class<?>[] groups() default {};
  Class<? extends Payload>[] payload() default {};
}

Validator

public class PasswordMatchValidator
    implements ConstraintValidator<PasswordMatch, UserRequest> {

  @Override
  public boolean isValid(UserRequest user, ConstraintValidatorContext context) {
    return user.getPassword().equals(user.getConfirmPassword());
  }
}
Class-level validation is frequently asked in interviews.

7️⃣ Handling Custom Validation Errors

Custom validation errors are handled the same way as built-in ones.

@RestControllerAdvice
public class GlobalExceptionHandler {

  @ExceptionHandler(MethodArgumentNotValidException.class)
  public Map<String, String> handleValidationErrors(
      MethodArgumentNotValidException ex) {

    Map<String, String> errors = new HashMap<>();

    ex.getBindingResult().getFieldErrors()
      .forEach(error ->
        errors.put(error.getField(), error.getDefaultMessage())
      );

    return errors;
  }
}

Deep dive: Global Exception Handling in Spring Boot


8️⃣ Common Mistakes Freshers Make

  • Putting business logic in controller instead of validator
  • Returning false for null instead of combining @NotNull
  • Using Entity instead of DTO for validation
  • Not writing reusable validators

9️⃣ Interview Questions on Custom Validation

  • Why create custom validation annotations?
  • Difference between field-level and class-level validation?
  • What is ConstraintValidator?
  • How is custom validation triggered?

Final Summary

  • Custom validation handles business rules cleanly
  • Validators keep controllers thin
  • Reusable, testable, and interview-friendly

If you can confidently explain custom validation annotations, you already stand out as a strong Spring Boot developer.


✅ Build Cleaner & Safer Spring Boot APIs

Custom validation annotations help eliminate repetitive logic and improve API reliability. Strengthen your Spring Boot skills by exploring these closely related REST and validation topics.

๐Ÿงพ Spring Boot REST API Validation Annotations

Learn built-in validation annotations and how they work alongside custom validators.

๐Ÿšจ Spring Boot Global Exception Handling

Handle validation errors consistently using @ControllerAdvice.

๐Ÿท️ Spring Boot Annotations for REST APIs

Understand commonly used Spring annotations for building clean REST APIs.

๐ŸŽฏ Controller vs RestController

Learn when to use @Controller and @RestController correctly.

๐ŸŽ“ Spring Boot Interview Questions (Freshers)

Prepare for entry-level interviews with commonly asked Spring Boot questions.

๐Ÿ’ผ Spring Boot Interview Questions (2–5 Years)

Interview questions covering validation, REST APIs, and real-world Spring Boot practices.