Comprehensive Guide to Spring Validation: Best Practices, Scenarios, and Advanced Features
This article provides an in‑depth tutorial on Spring Validation, covering basic usage, requestBody and requestParam validation, group and nested validation, collection handling, custom constraints, programmatic validation, fail‑fast mode, and the underlying implementation details within Spring MVC and Hibernate Validator.
Spring Validation is a Spring‑specific wrapper around the JSR‑303/JSR‑380 Bean Validation API (implemented by Hibernate Validator). It enables automatic validation of request parameters in Spring MVC controllers, turning constraint violations into HTTP 400 responses or custom error objects.
Simple usage and dependency – For Spring Boot < 2.3.x the hibernate-validator dependency is pulled in automatically; for newer versions you need to add it manually:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.1.Final</version>
</dependency>requestBody validation – Define a DTO with constraint annotations and annotate the controller method parameter with @RequestBody @Validated (or @Valid ). Example:
@Data
public class UserDTO {
@NotNull
@Length(min = 2, max = 10)
private String userName;
@NotNull
@Length(min = 6, max = 20)
private String account;
@NotNull
@Length(min = 6, max = 20)
private String password;
}
@PostMapping("/save")
public Result saveUser(@RequestBody @Validated UserDTO userDTO) {
// business logic executes only after successful validation
return Result.ok();
}requestParam / PathVariable validation – When parameters are passed via query string or URL path, place @Validated on the controller class and use constraint annotations on the method arguments:
@RestController
@Validated
public class UserController {
@GetMapping("{userId}")
public Result detail(@PathVariable("userId") @Min(10000000000000000L) Long userId) {
// logic after validation
return Result.ok();
}
@GetMapping("getByAccount")
public Result getByAccount(@Length(min = 6, max = 20) @NotNull String account) {
// logic after validation
return Result.ok();
}
}Unified exception handling – Convert validation exceptions into a consistent response format using @RestControllerAdvice :
@RestControllerAdvice
public class CommonExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
BindingResult bindingResult = ex.getBindingResult();
StringBuilder sb = new StringBuilder("Validation failed:");
for (FieldError fieldError : bindingResult.getFieldErrors()) {
sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", ");
}
return Result.fail(BusinessCode.PARAMETER_VALIDATION_FAILED, sb.toString());
}
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public Result handleConstraintViolationException(ConstraintViolationException ex) {
return Result.fail(BusinessCode.PARAMETER_VALIDATION_FAILED, ex.getMessage());
}
}Advanced features
Group validation – Define marker interfaces (e.g., Save , Update ) and assign them to constraint annotations via the groups attribute. Then invoke validation with @Validated(UserDTO.Save.class) or programmatically with Validator.validate(..., Save.class) .
Nested validation – Annotate nested object fields with @Valid so that their constraints are evaluated recursively.
Collection validation – Wrap a List in a custom class annotated with @Valid (e.g., ValidationList<E> ) to trigger element‑wise validation.
Custom constraint – Create an annotation like @EncryptId and a corresponding ConstraintValidator implementation to validate encrypted IDs.
Programmatic validation – Autowire javax.validation.Validator and call validate() manually for cases where annotation‑driven validation is insufficient.
Fail‑fast mode – Configure a ValidatorFactory with failFast(true) so that validation stops at the first violation.
Implementation details
For requestBody validation, Spring MVC’s RequestResponseBodyMethodProcessor reads the request, creates a WebDataBinder , and calls validateIfApplicable() . This method looks for @Validated or any annotation whose name starts with “Valid”, then delegates to WebDataBinder.validate() , which finally invokes Hibernate Validator’s validate() method.
Method‑level validation (e.g., validating individual method arguments) is implemented via AOP. MethodValidationPostProcessor registers a pointcut for beans annotated with @Validated and creates a MethodValidationInterceptor . The interceptor obtains an ExecutableValidator from the underlying Validator and calls validateParameters() and validateReturnValue() , again delegating to Hibernate Validator.
Thus, whether you validate a request body or a method argument, Spring Validation ultimately relies on Hibernate Validator to enforce the constraints, while providing a convenient Spring‑centric API and integration with the MVC exception handling flow.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.