Custom Validators
Write your own validation constraints.
Custom Validators is a free Spring Boot 4 Microservices & REST APIs lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Spring Boot 4 Microservices & REST APIs learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
When Built-ins Are Not Enough
Standard constraints cannot express every rule — a valid coupon code format, a phone number for a region, or a value that must exist in the database. For these you write a custom constraint backed by a validator.
Two Parts of a Custom Constraint
A custom constraint has two pieces:
- An annotation that you place on fields or parameters
- A ConstraintValidator that contains the actual checking logic
Defining the Annotation
Create the annotation and link it to its validator with @Constraint. Declare the standard message, groups, and payload members required by the spec.
@Target({ FIELD, PARAMETER })
@Retention(RUNTIME)
@Constraint(validatedBy = StrongPasswordValidator.class)
public @interface StrongPassword {
String message() default "Password is not strong enough";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}Implementing ConstraintValidator
The validator implements ConstraintValidator<Annotation, Type> and overrides isValid. Return true when the value satisfies the rule.
public class StrongPasswordValidator
implements ConstraintValidator<StrongPassword, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext ctx) {
if (value == null) return true; // let @NotNull handle null
return value.length() >= 8
&& value.chars().anyMatch(Character::isDigit)
&& value.chars().anyMatch(Character::isUpperCase);
}
}Null-Handling Convention
By convention a custom validator treats null as valid, delegating null-checking to @NotNull. This keeps constraints composable — each rule does one job.
Using the Custom Constraint
Apply your annotation just like a built-in one. Combined with @Valid on the controller, it participates in the normal validation flow.
public class CreateUserRequest {
@NotBlank
@StrongPassword
private String password;
}Custom Violation Messages
Inside isValid you can disable the default message and add a contextual one, useful when a single annotation enforces several sub-rules.
ctx.disableDefaultConstraintViolation();
ctx.buildConstraintViolationWithTemplate(
"Password must include a digit and an uppercase letter")
.addConstraintViolation();
return false;Injecting Beans into a Validator
A ConstraintValidator is a Spring bean, so it can use constructor injection — enabling rules that consult a repository or service, such as uniqueness checks.
public class UniqueEmailValidator
implements ConstraintValidator<UniqueEmail, String> {
private final UserRepository repo;
public UniqueEmailValidator(UserRepository repo) { this.repo = repo; }
@Override
public boolean isValid(String email, ConstraintValidatorContext ctx) {
return email == null || !repo.existsByEmail(email);
}
}Class-Level Constraints
Some rules span multiple fields — "password must equal confirmPassword." Target the annotation at TYPE and validate the whole object, accessing several fields together.
@Target(TYPE)
@Retention(RUNTIME)
@Constraint(validatedBy = PasswordsMatchValidator.class)
public @interface PasswordsMatch {
String message() default "Passwords do not match";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}Initialization Hook
Override initialize to capture attributes from the annotation instance, such as a configurable minimum length, before validation runs.
@Override
public void initialize(StrongPassword ann) {
this.minLength = ann.minLength();
}Keep Validators Pure and Fast
Validators run on every request, so keep them quick and side-effect-free where possible. Database-backed checks are fine but should be indexed and lightweight to avoid slowing request handling.
Quick Check
Test your understanding of custom validators.
Recap
Custom validators extend Bean Validation.
- A constraint is an
@Constraintannotation plus aConstraintValidator - Override
isValid; treatnullas valid by convention - Validators are beans, so they support constructor injection
- Use class-level constraints for cross-field rules
- Customize messages via the validation context
Frequently asked questions
Is the “Custom Validators” lesson free?
Yes — the full text of “Custom Validators” is free to read here on the web, and the Spring Boot 4 Microservices & REST APIs course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Spring Boot 4 Microservices & REST APIs course, upgrade to CoddyKit PRO.
What will I learn in “Custom Validators”?
Write your own validation constraints. You practise Spring Boot 4 Microservices & REST APIs with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Spring Boot 4 Microservices & REST APIs?
No prior experience is required. Spring Boot 4 Microservices & REST APIs on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Custom Validators” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Spring Boot 4 Microservices & REST APIs lesson?
Yes. Every Spring Boot 4 Microservices & REST APIs lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.