spring boot 参数校验需在controller层用@valid触发、分组校验适配多场景、统一异常处理返回友好json,并支持自定义注解、快速失败及service层手动校验。

Spring Boot 中参数校验不是加几个注解就完事,关键在“用对地方”“报对错误”“接得住反馈”。下面从实际开发高频场景出发,讲清楚怎么落地、避坑、调优。
基础校验:Controller 层用 @Valid + 注解最直接
这是最常用也最容易上手的方式。在 DTO(数据传输对象)上加校验注解,Controller 方法参数前加 @Valid 触发校验:
- 必须用 @Valid(不是 @Validated)触发级联校验;嵌套对象要加 @Valid 注解才能递归校验
- 常用注解:@NotBlank(字符串非空且非空白)、@NotNull(对象非 null)、@Min/@Max(数值范围)、@Email、@Pattern(正则)
- 别在实体类(Entity)上直接加校验注解——它属于持久层,和接口约束语义不同,容易耦合过重
分组校验:同一对象,不同接口不同规则
比如用户注册要校验手机号和密码,用户修改资料时手机号可不填,但邮箱必须填。这时用校验分组精准控制:
- 定义空接口作分组标记,如 public interface Create {} 和 public interface Update {}
- DTO 字段上指定 groups:@NotBlank(groups = Create.class)、@Email(groups = Update.class)
- Controller 方法中写:@Validated(Create.class) UserDTO dto —— Spring 就只校验该分组下的规则
统一异常处理:把校验失败转成友好的 JSON 响应
默认抛出 MethodArgumentNotValidException,前端收到的是 500 或堆栈,必须拦截并格式化:
- 用 @ControllerAdvice + @ExceptionHandler 捕获 MethodArgumentNotValidException
- 从 exception.getBindingResult().getFieldErrors() 提取字段名、错误信息(建议用 defaultMessage 或自定义 messageSource)
- 返回标准结构,例如:{"code":400,"msg":"参数错误","data":{"username":"用户名不能为空","age":"年龄必须在1~150之间"}}
- 注意:@Validated 在 @RequestBody 上生效才走这个异常;如果是 @RequestParam 多个参数,得用 BindingResult 紧跟其后,不能靠全局异常捕获
进阶技巧:自定义校验注解 + 快速失败 + 非 Controller 场景
基础注解不够用?需要查数据库判重、跨字段逻辑(如“结束时间不能早于开始时间”)?
- 写自定义注解(如 @PhoneExists)+ 对应 ConstraintValidator,validate() 方法里注入 Service 查库
- 想让校验“快速失败”(发现第一个错就停)?配置 spring.validation.fail-fast=true(仅对 @Validated 有效)
- Service 层也要校验?直接注入 javax.validation.Validator,手动调用 validate(),适合异步任务、MQ 消费等无 Controller 的上下文











