Backend Development 16 min read

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.

Top Architect
Top Architect
Top Architect
Spring Boot Parameter Validation with javax.validation Annotations

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

@Email

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.

BackendJavaSpring BootAnnotationsParameter Validationjavax.validation
Top Architect
Written by

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.

0 followers
Reader feedback

How this landed with the community

login 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.