Beyond Controller Validation: 90% Miss Spring’s Advanced Validation Techniques

The article explains how standard Spring Boot controller‑level validation leaves service methods unchecked, and demonstrates using @Validated with MethodValidationPostProcessor, custom exception handling, and a MethodValidationExcludeFilter to apply, convert, and selectively disable validation across the application.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Beyond Controller Validation: 90% Miss Spring’s Advanced Validation Techniques

In Spring Boot projects, parameter validation is essential for ensuring request data integrity, but the default approach only validates at the @Controller layer, leaving service‑level methods unprotected and potentially causing business logic errors.

1. Introduction

Typical controller validation uses @Validated and BindingResult, as shown:

@PostMapping("/create")
public Object create(@Validated @RequestBody User user, BindingResult result) {
    if (result.hasErrors()) {
        return result.getFieldErrors()
                     .stream()
                     .map(error -> error.getField() + "," + error.getDefaultMessage())
                     .toList();
    }
    return user;
}

This approach cannot validate parameters in @Service or other non‑controller beans.

2. Practical Cases

2.1 Validation in Non‑Controller Methods

After adding the spring-boot-starter-validation dependency, Spring automatically registers a MethodValidationPostProcessor that validates beans annotated with @Validated. Applying it to a service class is as simple as:

@Service
@Validated
public class UserService {
    public void save(@Valid User user) {
        // ...
    }
    public void query(@NotEmpty(message = "Keyword must be provided") String keyword) {
        // ...
    }
}

When these methods are invoked, Spring validates the parameters and throws ConstraintViolationException on failure.

private final UserService userService;
public ValidationController(UserService userService) { this.userService = userService; }
@PostMapping("/create")
public Object create(@RequestBody User user) {
    this.userService.save(user);
    return user;
}
@ExceptionHandler(ConstraintViolationException.class)
public Object violationException(ConstraintViolationException e) {
    return e.getConstraintViolations().stream()
            .map(cv -> cv.getPropertyPath() + "," + cv.getMessage())
            .toList();
}

The console shows the ConstraintViolationException details, and the response contains the formatted error messages.

2.2 Exception Conversion

To unify exception handling, Spring can adapt ConstraintViolationException to MethodValidationException by enabling the following property:

spring:
  validation:
    method:
      adapt-constraint-violations: true

Then define a handler for MethodValidationException:

@ExceptionHandler(MethodValidationException.class)
public Object validationException(MethodValidationException e) {
    return e.getAllErrors().stream()
            .map(err -> err.getDefaultMessage())
            .toList();
}

This returns a simple list of validation messages.

2.3 Custom Method‑Validation Exclude Filter

If third‑party libraries or framework components are annotated with @Validated but should be excluded from validation, implement MethodValidationExcludeFilter:

@Component
public class PackMethodValidationExcludeFilter implements MethodValidationExcludeFilter {
    @Value("${pack.validation.excludes:}")
    private Set<String> excludes;
    @Override
    public boolean isExcluded(Class<?> type) {
        return excludes.contains(type.getPackageName());
    }
}

Configure the packages to skip:

pack:
  validation:
    excludes: com.pack.validation

When a bean belongs to an excluded package, its methods are not validated, as demonstrated by the same controller call producing no validation errors for the filtered class.

Alternatively, the interface provides static factory methods to create filters based on annotation presence:

public interface MethodValidationExcludeFilter {
    static MethodValidationExcludeFilter byAnnotation(Class<? extends Annotation> annotationType) {
        return byAnnotation(annotationType, SearchStrategy.INHERITED_ANNOTATIONS);
    }
    static MethodValidationExcludeFilter byAnnotation(Class<? extends Annotation> annotationType, SearchStrategy searchStrategy) {
        return (type) -> MergedAnnotations.from(type, searchStrategy).isPresent(annotationType);
    }
}

These utilities allow fine‑grained control over which beans participate in method‑level validation.

Overall, the article demonstrates how to extend Spring Boot’s validation beyond controllers, unify exception handling, and selectively disable validation for specific packages, providing a more robust and flexible validation strategy for complex applications.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaException HandlingvalidationSpring BootCustom FilterMethodValidationPostProcessor
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.