Master Spring Bean Validation: Quick Start, Custom Constraints, and Advanced Techniques
This article walks through Spring Bean Validation fundamentals, demonstrating how to apply built‑in constraints, create custom validators, use group validation, perform cascading request validation, and enable method‑level checks, all with clear code examples and best‑practice recommendations.
01 Bean Validation Basics: Quick Start and Efficient Validation
Spring Validation integrates the JSR‑380 (Bean Validation 2.0) specification and provides a set of ready‑to‑use validation annotations that help developers quickly enforce basic data checks.
@Data
public class UserDTO {
@NotNull(message = "User ID cannot be null")
private Long id;
@NotBlank(message = "Username cannot be blank")
@Size(min = 4, max = 20, message = "Username length must be between 4 and 20 characters")
private String username;
@Email(message = "Invalid email format")
private String email;
@Min(value = 18, message = "Age must be at least 18")
@Max(value = 120, message = "Age must be at most 120")
private Integer age;
@Past(message = "Birth date must be in the past")
private LocalDate birthDate;
@Pattern(regexp = "^1[3-9]\d{9}$", message = "Invalid phone number format")
private String phoneNumber;
} @RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
public ResponseEntity createUser(@RequestBody @Valid UserDTO userDTO, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
throw new ValidationException(bindingResult);
}
return ResponseEntity.ok(userDTO);
}
}Annotations: Use @NotNull, @NotBlank, @Email, etc., on DTO fields to perform quick basic validation.
In the controller, apply @Valid on the request body and retrieve validation results via BindingResult. If validation fails, throw a custom ValidationException to inform the client.
Best Practices: Provide meaningful error messages, keep a consistent naming style, and place validation annotations on DTOs rather than entity classes to improve separation of concerns and maintainability.
02 Custom Constraint Validators: Tailored Business Rules
Spring Validation allows developers to create custom constraints to satisfy specific business validation needs.
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueUsernameValidator.class)
public @interface UniqueUsername {
String message() default "Username already exists";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
} public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {
private UserRepository userRepository;
@Override
public boolean isValid(String username, ConstraintValidatorContext context) {
if (username == null) {
return true;
}
return !userRepository.existsByUsername(username);
}
} public class UserRegistrationDTO {
@NotBlank
@Size(min = 4, max = 20)
@UniqueUsername
private String username;
}Apply @UniqueUsername on the username field of UserRegistrationDTO to enforce uniqueness.
03 Group Validation: Switching Rules per Scenario
Group validation enables different validation rules for different use cases, such as create versus update operations.
public interface ValidationGroups {
interface Create {}
interface Update {}
} @Data
public class ProductDTO {
@Null(groups = ValidationGroups.Create.class, message = "ID must be null when creating")
@NotNull(groups = ValidationGroups.Update.class, message = "ID cannot be null when updating")
private Long id;
@NotBlank(groups = {ValidationGroups.Create.class, ValidationGroups.Update.class})
private String name;
@PositiveOrZero(groups = ValidationGroups.Create.class)
@Positive(groups = ValidationGroups.Update.class)
private BigDecimal price;
} @RestController
@RequestMapping("/api/products")
public class ProductController {
@PostMapping
public ResponseEntity createProduct(@RequestBody @Validated(ValidationGroups.Create.class) ProductDTO productDTO) {
return ResponseEntity.ok(productDTO);
}
@PutMapping("/{id}")
public ResponseEntity<ProductDTO> updateProduct(@PathVariable Long id,
@RequestBody @Validated(ValidationGroups.Update.class) ProductDTO productDTO) {
return ResponseEntity.ok(productDTO);
}
}Use @Validated with the appropriate group to trigger the correct set of constraints.
04 Cascading Validation: Deep Object Graph Checks
Use @Valid on nested objects to ensure validation traverses the entire request structure.
@Data
public class OrderDTO {
@NotNull
private Long id;
@NotNull
@Valid
private CustomerDTO customer;
@NotEmpty
@Valid
private List<OrderItemDTO> items;
}
@Data
public class CustomerDTO {
@NotNull
private Long id;
@NotBlank
private String name;
@Email
private String email;
@Valid
private AddressDTO address;
}Annotate fields like customer and items with @Valid so that their internal constraints are also evaluated.
05 Method‑Level Validation: Service Layer Robustness
Enable method‑level validation to verify parameters and return values of service methods.
@Configuration
@EnableMethodValidation
public class ValidationConfig {} @Service
public class UserService {
@Validated
public User createUser(@Valid UserDTO userDTO) {
return new User();
}
@NotNull
public User findById(@Min(1) Long id) {
return new User();
}
@Validated(ValidationGroups.Update.class)
public void updateUser(@Valid UserDTO userDTO) {}
}Annotate the configuration class with @EnableMethodValidation and service methods with @Validated, @Valid, @NotNull, @Min, etc., to enforce constraints at the service layer.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
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.
