导读:本期聚焦于半夏创作的《Spring Boot 如何整合 Validation 实现参数校验?完整配置与实战详解》,敬请观看详情。接口收到一坨脏数据直接入库,最后查询报错才发现问题,这种排查成本谁都不想承担。参数校验放在最前面,能把无效请求挡在业务逻辑之外。本文围绕 Spring Boot 整合 Validation 展开,介绍依赖引入方式、常用注解清单、@Valid 与 @Validated 的区别、分组校验与级联校验的用法,并演示全局异常处理器如何把校验失败信息统一返回给前端,同时附上自定义校验注解的开发步骤,帮你搭建一套完整的校验体系。

参数校验是每个后端服务都绕不开的环节。早期写接口时,很多人习惯在方法体里写一堆 if 判断,字段非空、长度限制、格式匹配,层层嵌套下来代码又长又难维护。JSR 380(Bean Validation 2.0)规范提供了一套声明式的校验方案,只需要在实体类字段上打注解,校验逻辑就能自动执行。Spring Boot 对这套规范做了良好封装,本文完整演示如何整合使用。

Spring Boot 如何整合 Validation 实现参数校验?完整配置与实战详解

一、引入依赖与基础配置

从 Spring Boot 2.3 版本开始,spring-boot-starter-web 不再默认携带校验相关的依赖,需要手动引入。引入方式很简单,在 pom.xml 中添加以下依赖即可:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

这个 starter 底层使用 Hibernate Validator 作为具体实现。引入后不需要任何额外配置,Spring Boot 会自动完成装配。如果你使用的是 Spring Boot 2.3 之前的版本,web starter 已经包含了 validation,无需重复引入。

校验失败时默认会抛出 MethodArgumentNotValidException(用于 RequestBody 参数)或 ConstraintViolationException(用于方法级参数校验),如果不做处理,前端拿到的就是一堆不友好的报错堆栈。所以通常还需要配置全局异常处理,这部分在后面会讲到。

二、常用校验注解与基本用法

先定义一个用户注册的实体类,把常用注解都展示一遍:

public class UserDTO {

    @NotNull(message = "用户名不能为空")
    private String username;

    @NotBlank(message = "昵称不能为空白")
    @Size(min = 2, max = 20, message = "昵称长度必须在2到20之间")
    private String nickname;

    @NotNull(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;

    @NotNull(message = "年龄不能为空")
    @Min(value = 1, message = "年龄最小为1")
    @Max(value = 120, message = "年龄最大为120")
    private Integer age;

    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
    private String phone;

    // 省略 getter 和 setter
}

这里要特别注意几个容易混淆的注解区别:@NotNull 只检查对象是否为 null,空字符串能通过校验;@NotEmpty 要求不为 null 且长度大于 0,用在字符串、集合、数组上;@NotBlank 只能用于字符串,会去掉首尾空格后判断是否为空。比如一个字段填了三个空格,@NotNull 和 @NotEmpty 都能通过,只有 @NotBlank 会拦截。

在 Controller 中使用时,给参数对象加上 @Validated@Valid 注解即可触发校验:

@RestController
@RequestMapping("/user")
public class UserController {

    @PostMapping("/register")
    public String register(@RequestBody @Validated UserDTO userDTO) {
        return "注册成功";
    }
}

当请求参数不满足约束条件时,请求会在进入方法体之前被拦截,根本不会执行业务逻辑,这样就把脏数据挡在了最外层。除了上面这些,常用注解还包括 @AssertTrue@DecimalMin@Past(必须是过去时间)、@Future(必须是未来时间)、@Size 等,基本覆盖了日常场景。

三、@Valid 与 @Validated 的区别及分组校验

这两个注解经常被拿来比较。@Valid 是 JSR 标准注解,定义在 jakarta.validation 包中;@Validated 是 Spring 提供的扩展注解。功能上的核心差异有两点:第一,@Validated 支持分组校验,@Valid 不支持;第二,@Validated 可以直接标注在类上开启方法级参数校验,@Valid 只能标注在参数或字段上做级联校验。

分组校验解决的是同一个实体类在不同场景下校验规则不同的问题。比如新增用户时不需要传 id,修改时必须传 id。先定义分组接口:

public interface AddGroup {}
public interface UpdateGroup {}

public class UserDTO {

    @Null(groups = {AddGroup.class}, message = "新增时不能指定id")
    @NotNull(groups = {UpdateGroup.class}, message = "修改时必须指定id")
    private Long id;

    @NotNull(groups = {AddGroup.class, UpdateGroup.class}, message = "用户名不能为空")
    private String username;

    // 省略其他字段
}

Controller 中指定分组,只有属于该分组的约束才会生效,没有指定分组的默认约束在分组模式下不会被校验,这一点很多人踩过坑:

@PostMapping("/add")
public String add(@RequestBody @Validated(AddGroup.class) UserDTO userDTO) {
    return "新增成功";
}

@PostMapping("/update")
public String update(@RequestBody @Validated(UpdateGroup.class) UserDTO userDTO) {
    return "修改成功";
}

级联校验也很实用。当实体类中嵌套了另一个对象时,在内层字段上标注 @Valid,校验会递归地进行到嵌套对象内部。注意这里只能用 @Valid,@Validated 没有级联能力,这也是两者配合使用的典型场景。

四、统一异常处理与自定义校验注解

校验失败后给前端返回统一格式的提示信息,是生产环境的基本要求。通过 @RestControllerAdvice 全局捕获异常即可实现:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleValidException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        String message = bindingResult.getFieldErrors().stream()
                .map(error -> error.getField() + ": " + error.getDefaultMessage())
                .collect(Collectors.joining("; "));
        return Result.fail(400, message);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    public Result handleConstraintException(ConstraintViolationException e) {
        String message = e.getConstraintViolations().stream()
                .map(v -> v.getMessage())
                .collect(Collectors.joining("; "));
        return Result.fail(400, message);
    }
}

内置注解覆盖不到的业务规则,比如手机号、身份证等特定格式,可以通过自定义注解扩展。先定义注解,再写一个校验器实现 ConstraintValidator 接口:

@Documented
@Constraint(validatedBy = PhoneValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Phone {
    String message() default "手机号格式不正确";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

public class PhoneValidator implements ConstraintValidator<Phone, String> {

    private static final Pattern PATTERN = Pattern.compile("^1[3-9]\\d{9}$");

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return true; // 空值交给 @NotNull 处理
        }
        return PATTERN.matcher(value).matches();
    }
}

使用时和内置注解完全一样,直接标注在字段上即可。自定义注解的好处是把校验规则收敛到一处,规则变更时只改一个类,比在各个接口里写正则判断干净得多。

整体来看,Spring Boot 整合 Validation 的成本很低,两个依赖加几行注解就能跑起来,再配合分组校验、级联校验和全局异常处理,可以覆盖绝大多数参数校验场景,值得在每个项目中落地。

Spring Boot Validation参数校验@Validated修改时间:2026-09-05 02:50:35

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260905/50627.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。