参数验证是 Java Web 应用的第一道防线,其核心价值在于:抵御恶意行为、保证业务数据合法性、简化代码并提升用户体验。在实际开发中,通常结合 Spring 的 @Validated 与 JSR-303 注解(如 @NotNull、@Pattern)实现自动化参数验证,既规范又高效。

JSR 303 是 Java 规范提案(Java Specification Requests) 中的一项标准,全称为 Bean Validation 1.0,它定义了一套用于 JavaBean 数据校验的元数据模型和 API。其核心目标是通过注解的方式声明对象属性的校验规则,从而替代繁琐的手动校验逻辑(如大量 if-else 判断),让代码更简洁、可读性更强。

JSR 303 只是规范,需要具体的实现类才能生效。最常用的实现是 Hibernate Validator。在 Spring Boot 应用中,无需手动添加 Hibernate Validator 依赖,可以通过导入 spring-boot-starter-validation 的方式来使用,它包含了 Spring Boot 参数校验所需的全部依赖组件。

Spring Web MVC 参数验证_数据校验

校验参数需要用到 jakarta.validation 包下的约束注解,它们既可以用在方法参数上,也可以用在参数类成员变量上。

Spring Web MVC 参数验证_数据校验_02

参数验证场景

方法参数验证

设置验证规则 name 不能为 null

@RestController
public class HelloController {

  @RequestMapping(value = { "/hello" }, method = RequestMethod.POST)
  public String requestMethodName(@NotNull String name) {
    return "Hello, Spring!";
  }

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

请求的时候不传 name 参数

Spring Web MVC 参数验证_Spring MVC_03

这时接口就会直接返回 Validation failure 错误。

对象验证

// 实体类成员上添加验证注解
public class User {

  private Long id;
	// 姓名不能为空
  @NotNull
  private String name;

  // 年龄不能小于18,并且不能大于120
  @Min(18)
  @Max(120)
  private Integer age;

}

// 控制器方法参数上添加 @Validated 注解开启验证
@RestController
@RequestMapping("/user")
public class UserController {

  @PostMapping("")
  public String addUser(@Validated @RequestBody User entity) {
    return "add success";
  }

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.

嵌套对象验证

在需要验证的嵌套成员上使用 @Valid 注解触发嵌套对象的验证。

// User.java
@Data
public class User {

  private Long id;

  @NotNull
  private String name;

  @Min(18)
  @Max(120)
  private Integer age;

  @NotNull
  @Valid // 触发对嵌套对象的验证
  private Contact contact;
}

// Contact.js
@Data
public class Contact {
  
  @NotBlank
  private String address;

  @NotBlank
  private String phone;

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.

分组验证

当一个实体类需要在不同场景使用不同验证规则时,就需要利用 @Validated 的分组验证功能了,例如用户的 “新增” 和 “修改”。嵌套对象的验证也需要定义分组规则。

// User.java 
// 在实体类中定义分组规则
public class User {

  // 定义空接口作为分组
  public interface Add {}
  public interface Update {}

  // 定义分组规则,仅修改时验证
  @NotNull(groups = { Update.class })
  private Long id;

  // 定义分组规则,新增和修改时均验证
  @NotNull(groups = { Add.class, Update.class })
  private String name;

  @Min(18)
  @Max(120)
  // 定义分组规则,新增和修改时均验证
  @NotNull(groups = { Add.class, Update.class })
  private Integer age;
  
  @Valid
  @NotNull(groups = { Add.class, Update.class })
  private Contact contact;

}

// Contact.java
public class Contact {
  
  @NotBlank
  private String address;

  @NotBlank(groups = { User.Add.class, User.Update.class })
  @Phone(groups = { User.Add.class, User.Update.class })
  private String phone;

}

// UserContrller.java
// 在控制器中指定分组
@RestController
@RequestMapping("/user")
public class UserController {

  @PostMapping("")
  // 应用新增验证规则
  public String addUser(@Validated(User.Add.class) @RequestBody User entity) {
    return "add success";
  }

  @PutMapping("")
  // 应用修改验证规则
  public String updateUser(@Validated(User.Update.class) @RequestBody User entity) {
    return "update success";
  }

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.

自定义验证

自定义验证需要创建“注解”和“验证器”两个文件。

// Phone.java
// 规则注解
@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class) // 指定校验器
public @interface Phone {
  String message() default "手机号格式错误";

  Class<?>[] groups() default {};

  Class<? extends Payload>[] payload() default {};
}

// PhoneValidator.java
// 实现验证器
public class PhoneValidator implements ConstraintValidator<Phone, String> {
  @Override
  public boolean isValid(String value, ConstraintValidatorContext context) {
    return value != null && value.matches("^1[3-9]\\d{9}$");
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.