Mastering Parameter Validation in Spring Boot with JSR‑303 & Hibernate Validator
This guide explains how to use JSR‑303 and Hibernate Validator in Java Spring Boot to perform comprehensive parameter validation, covering basic annotations, global exception handling, group validation, nested object checks, fast‑fail configuration, and custom validation rules, with practical code examples and testing procedures.
Parameter Validation Overview
In real projects the first step after receiving data is to validate its correctness. While simple checks can be done with annotations, complex business rules (e.g., total amount = quantity * unit price) require a validator. Spring supports JSR‑303, a standard Bean Validation framework, which allows declarative constraints such as @NotNull, @Max, etc.
Basic JSR‑303 Constraints
@Null : the annotated element must be null
@NotNull : the annotated element must not be null
@AssertTrue : the annotated element must be true
@AssertFalse : the annotated element must be false
@Min(value) : numeric value must be greater than or equal to the specified minimum
@Max(value) : numeric value must be less than or equal to the specified maximum
@DecimalMin(value) : numeric value must be greater than or equal to the specified minimum (as a decimal)
@DecimalMax(value) : numeric value must be less than or equal to the specified maximum (as a decimal)
@Size(min, max) : the size of the annotated element must be within the given range
@Digits(integer, fraction) : numeric value must have the specified number of integer and fractional digits
@Past : the annotated date must be in the past
@Future : the annotated date must be in the future
@Pattern(value) : the annotated string must match the given regular expression
Hibernate Validator Extensions
@NotBlank : the string must not be null and must contain at least one non‑whitespace character
@Email : the string must be a valid email address
@URL : the string must be a valid URL
@Length : the string length must be within the specified range
@NotEmpty : collection, array or string must not be empty
@Range : the numeric value must be within the specified range
Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>Global Exception Handling
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
// Parameter validation exception
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public Result error(MethodArgumentNotValidException e){
log.warn(e.getMessage());
return Result.fail()
.code(ResultCodeEnum.ARGUMENT_VALID_ERROR.getCode())
.message(e.getBindingResult().getFieldError().getDefaultMessage());
}
@ExceptionHandler(BindException.class)
@ResponseBody
public Result error(BindException e){
log.warn(e.getMessage());
StringBuilder sb = new StringBuilder();
for (ObjectError error : e.getBindingResult().getAllErrors()) {
sb.append(error.getDefaultMessage());
}
return Result.fail()
.code(ResultCodeEnum.ARGUMENT_VALID_ERROR.getCode())
.message(sb.toString());
}
@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
public Result error(ConstraintViolationException e){
log.warn(e.getMessage());
StringBuilder sb = new StringBuilder();
for (ConstraintViolation<?> violation : e.getConstraintViolations()) {
sb.append(violation.getMessage());
}
return Result.fail()
.code(ResultCodeEnum.ARGUMENT_VALID_ERROR.getCode())
.message(sb.toString());
}
@ExceptionHandler(Exception.class)
@ResponseBody
public Result error(Exception e){
log.warn(e.getMessage());
return Result.fail().message("执行了全局异常处理");
}
}Fast‑Fail Configuration
@Configuration
public class ValidConfig {
/**
* Fast‑fail validator
*/
@Bean
public Validator validator(){
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
.configure()
// enable fast‑fail mode
.failFast(true)
.buildValidatorFactory();
return validatorFactory.getValidator();
}
/**
* Method validation post‑processor using the fast‑fail validator
*/
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor(){
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setValidator(validator());
return processor;
}
}Group Validation
// Group definitions
public class UserGroup {
public interface CreateGroup extends Default {}
public interface UpdateGroup extends Default {}
}
// Unified VO with groups
@Data
public class UserVo {
@NotBlank(message = "id不能为空", groups = UserGroup.UpdateGroup.class)
private String id;
@NotBlank(message = "用户名不能为空", groups = {UserGroup.CreateGroup.class, UserGroup.UpdateGroup.class})
private String userName;
@NotBlank(message = "姓名不能为空", groups = {UserGroup.CreateGroup.class, UserGroup.UpdateGroup.class})
private String name;
@Size(min=11, max=11, message = "手机号长度不符合要求", groups = {UserGroup.CreateGroup.class, UserGroup.UpdateGroup.class})
private String phone;
@NotNull(message = "性别不能为空", groups = {UserGroup.CreateGroup.class, UserGroup.UpdateGroup.class})
private Integer sex;
}
// Controller using groups
@Validated
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping("create")
public Result createUser(@Validated(UserGroup.CreateGroup.class) @RequestBody UserVo userVO){
return Result.ok("参数校验成功");
}
@PutMapping("update")
public Result updateUser(@Validated(UserGroup.UpdateGroup.class) @RequestBody UserVo userVO){
return Result.ok("参数校验成功");
}
@GetMapping("getUserById/{id}")
public Result getUserById(@PathVariable @Size(min = 2, max = 5, message = "id长度不符合要求") String id){
return Result.ok("参数校验成功");
}
}Nested Object Validation
@Data
public class Role {
@NotBlank(message = "角色名称不能为空")
private String roleName;
}
@Data
public class UserVo {
// other fields omitted for brevity
@Valid
@NotNull(message = "角色信息不能为空")
private Role role;
}Custom Validation Rule
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Constraint(validatedBy = PhoneValidator.class)
public @interface PhoneValid {
String message() default "请填写正确的手机号";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class PhoneValidator implements ConstraintValidator<PhoneValid, Object> {
public static final String REGEX_PHONE = "^(13\\d|14[579]|15[^4\\D]|17[^49\\D]|18\\d)\\d{8}$";
@Override
public void initialize(PhoneValid constraintAnnotation) {}
@Override
public boolean isValid(Object o, ConstraintValidatorContext ctx) {
String phone = String.valueOf(o);
if (phone.length() != 11) return false;
return phone.matches(REGEX_PHONE);
}
}
@Data
public class UserVo {
// other fields omitted
@PhoneValid(message = "手机号格式错误")
private String phone;
}Testing Screenshots
Examples of successful and failed validation are shown in the following images:
These screenshots demonstrate that validation rules are applied correctly, including group validation, path variable checks, nested object validation, and custom phone number validation.
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.
Alibaba Cloud Developer
Alibaba's official tech channel, featuring all of its technology innovations.
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.
