Implementing custom validation logic for a Spring Boot endpoint using a combination of JSR-303 and Spring's Validator
Data Structures & Algorithms practice on Codemia
Step through 300+ coding interview problems with animated visualisers that show the data structure changing as the code runs. The Blind 75 are free.
Introduction
In Spring Boot, bean validation annotations handle a lot of field-level rules well, but they are not enough for every endpoint. Cross-field checks, conditional rules, and domain-specific validation often need extra logic. A good pattern is to keep simple declarative rules in JSR-303 annotations and layer more complex endpoint validation through Spring's Validator interface.
Use JSR-303 for Simple Structural Rules
Start by putting basic field constraints directly on the request DTO.
This keeps obvious shape validation close to the data model.
Add Spring Validator for Custom Logic
Now add rules that do not fit cleanly into a single annotation, such as matching passwords or business-specific conditions.
This is where you place logic that depends on several fields or on domain-specific policy.
Wire It into the Endpoint
A controller can run both styles of validation by using @Valid and then invoking the custom validator.
The sequence matters:
- annotation-based validation runs
- custom validator adds extra errors
- the controller decides whether to continue
Why This Split Is Useful
Separating the validation styles keeps the code easier to reason about.
Use annotations for:
- null checks
- length constraints
- format checks
- simple field requirements
Use a Spring validator for:
- cross-field consistency
- conditional rules
- repository-backed checks
- business-specific rules that are awkward as annotations
That division usually produces cleaner DTOs and more maintainable validation code.
Alternative: Custom Bean Validation Annotation
If a rule is reused across many DTOs, a custom bean validation annotation may be better than a Spring Validator. But for endpoint-specific logic, the Spring validator approach is often simpler and more direct.
In other words:
- reusable domain rule across many places: consider a custom constraint annotation
- endpoint-specific orchestration rule: Spring validator is often enough
Common Pitfalls
- Stuffing every rule into custom validators and losing the simplicity of built-in annotations.
- Putting complex cross-field logic into one field annotation where the intent becomes hard to read.
- Forgetting to inspect
BindingResultafter running the custom validator. - Doing expensive database lookups in validation without being clear about the performance cost.
- Mixing validation and business side effects. Validation should report problems, not create or change state.
Summary
- Use JSR-303 annotations for straightforward field-level validation.
- Use Spring's
Validatorfor cross-field and endpoint-specific rules. - Call the custom validator after
@Validso both validation layers contribute to the sameBindingResult. - Keep validation declarative where possible and custom where necessary.
- The best combination is usually not all annotations or all custom code, but a clear split between simple and domain-specific rules.
Related reading
- implementing debounce in Java
- Implementing Naïve Bayes algorithm in Java - Need some guidance
- Implementing non-blocking retry with backoff with spring-amqp and rabbitmq
- Implementing Singleton with an Enum in Java
- implements Closeable or implements AutoCloseable
- implements Runnable vs extends Thread in Java
- Implements vs extends When to use? What's the difference?
- Import Maven dependencies in IntelliJ IDEA

OOD Fundamentals
Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.
View the courseData Structures & Algorithms practice on Codemia
Step through 300+ coding interview problems with animated visualisers that show the data structure changing as the code runs. The Blind 75 are free.