Spring Boot Parameter Validation with javax.validation Annotations
This article explains how to use the javax.validation (JSR‑303) annotation‑based validation framework in Spring Boot to replace verbose manual checks, covering built‑in constraints, Maven dependencies, DTO annotations, custom validators, validation groups, global exception handling, and practical RESTful examples.
When a large number of request parameters need validation, manually checking each field and throwing exceptions leads to repetitive and hard‑to‑maintain code. Spring Boot’s javax.validation annotations provide a concise, declarative way to perform these checks.
Why use a validator
The javax.validation API offers a set of annotations (e.g., @NotNull , @NotBlank , @Pattern ) that automatically validate bean properties, eliminating the need for explicit if‑else validation logic.
Manual validation example (for contrast)
@PostMapping("/save/serial")
public Object save(@RequestBody UserVO userVO) {
String mobile = userVO.getMobile();
if (StringUtils.isBlank(mobile)) {
return RspDTO.paramFail("mobile:手机号码不能为空");
} else if (!Pattern.matches("^[1][3-9][0-9]{9}$", mobile)) {
return RspDTO.paramFail("mobile:手机号码格式不对");
}
// ... other checks ...
userService.save(userVO);
return RspDTO.success();
}JSR‑303 basics
JSR‑303 defines standard validation annotations that can be placed on JavaBean fields. Spring Boot already includes the necessary dependencies, but they can also be added manually:
<!-- jsr 303 -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<!-- hibernate validator -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.0.Final</version>
</dependency>Common constraint annotations
Annotation
Target Types
Description
@NotNull
Any type
Value must not be null
@NotEmpty
String, Collection, Map, Array
Value must not be null and size > 0
@NotBlank
String
Value must not be null and must contain non‑whitespace characters
@Size(min, max)
String, Collection, Map, Array
Length or size must be within the specified range
@Pattern(regexp)
String
Value must match the given regular expression
String
Value must be a well‑formed email address
@Min / @Max
Numeric types
Value must be greater than or equal to / less than or equal to the specified bound
@Future / @Past
Date, Calendar
Value must be in the future / past
@Valid
Any object
Cascade validation to nested objects
Practical example
Define a DTO with validation constraints:
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.*;
import java.io.Serializable;
import java.util.Date;
@Data
public class UserDTO implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull(message = "用户id不能为空")
private Long userId;
@NotBlank(message = "用户名不能为空")
@Length(max = 20, message = "用户名不能超过20个字符")
@Pattern(regexp = "^[\\u4E00-\\u9FA5A-Za-z0-9\\*]*$", message = "用户名只能包含文字、字母和数字")
private String username;
@NotBlank(message = "手机号不能为空")
@Pattern(regexp = "^[1][3-9][0-9]{9}$", message = "手机号格式有误")
private String mobile;
private String sex;
@NotBlank(message = "联系邮箱不能为空")
@Email(message = "邮箱格式不对")
private String email;
private String password;
@Future(message = "时间必须是将来时间")
private Date createTime;
}Validate the DTO in a controller method:
@PostMapping("/save/valid")
public RspDTO save(@RequestBody @Validated UserDTO userDTO) {
userService.save(userDTO);
return RspDTO.success();
}Global exception handling
Handle validation exceptions uniformly with a @RestControllerAdvice class:
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final int PARAM_FAIL_CODE = 1002;
private static final int VALIDATION_CODE = 1003;
@ExceptionHandler(MethodArgumentNotValidException.class)
public RspDTO handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
return new RspDTO(PARAM_FAIL_CODE, e.getBindingResult().getFieldError().getDefaultMessage());
}
@ExceptionHandler(ValidationException.class)
public RspDTO handleValidationException(ValidationException e) {
return new RspDTO(VALIDATION_CODE, e.getCause().getMessage());
}
@ExceptionHandler(ConstraintViolationException.class)
public RspDTO handleConstraintViolationException(ConstraintViolationException e) {
return new RspDTO(PARAM_FAIL_CODE, e.getMessage());
}
// other handlers omitted for brevity
}Custom validation annotation
Define an annotation interface:
@Documented
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IdentityCardNumberValidator.class)
public @interface IdentityCardNumber {
String message() default "身份证号码不合法";
Class
[] groups() default {};
Class
[] payload() default {};
}Implement the validator logic:
public class IdentityCardNumberValidator implements ConstraintValidator
{
@Override
public void initialize(IdentityCardNumber constraintAnnotation) {}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
return IdCardValidatorUtils.isValidate18Idcard(value.toString());
}
}Use the custom annotation on a DTO field:
@NotBlank(message = "身份证号不能为空")
@IdentityCardNumber(message = "身份证信息有误,请核对后提交")
private String clientCardNo;Validation groups
Define group interfaces:
public interface Create extends Default {}
public interface Update extends Default {}Apply groups to constraints and trigger them with @Validated(Group.class) :
@PostMapping("/update/groups")
public RspDTO update(@RequestBody @Validated(Update.class) UserDTO userDTO) {
userService.updateById(userDTO);
return RspDTO.success();
}When a controller needs to validate multiple parameters, annotate the class with @Validated and use constraint annotations on method arguments:
@GetMapping("/get")
public RspDTO getUser(@RequestParam("userId") @NotNull(message = "用户id不能为空") Long userId) {
User user = userService.selectById(userId);
if (user == null) {
return new RspDTO
().nonAbsent("用户不存在");
}
return new RspDTO
().success(user);
}Conclusion
Using javax.validation with Spring Boot greatly simplifies parameter validation, reduces boilerplate, and centralizes error handling. Combining built‑in constraints, custom annotations, and validation groups provides a flexible and maintainable solution for robust backend services.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
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.