Spring Boot 参数校验 Validation 终极指南
1. 概述
Spring Validation 基于 JSR-303(Bean Validation)规范,通过@Validated注解实现声明式校验。核心优势:
- 零侵入性:基于 AOP 实现方法拦截校验
- 规范统一:兼容 Bean Validation 标准注解
- 功能扩展:支持分组校验、嵌套校验等高级特性
- 高效开发:减少 80% 的参数校验代码量
💡 关键区别:
@Validated是 Spring 对@Valid的增强封装,支持分组校验,而@Valid支持嵌套校验
2. 注解体系
2.1 Bean Validation 标准注解
| 分类 | 注解 | 说明 |
|---|---|---|
| 空值检查 | @NotBlank | 字符串非空且 trim() 后长度 > 0(仅适用于字符串) |
@NotEmpty | 集合/数组元素数 > 0,字符串长度 > 0(适用于集合、数组、字符串) | |
@NotNull | 字段值不能为 null | |
@Null | 字段值必须为 null | |
| 数值检查 | @DecimalMax(value) | 数值必须 ≤ 指定值(支持小数) |
@DecimalMin(value) | 数值必须 ≥ 指定值(支持小数) | |
@Digits(integer,fraction) | 整数部分最多 integer 位,小数部分最多 fraction 位 | |
@Positive | 必须为正数 | |
@PositiveOrZero | 必须为正数或 0 | |
@Max(value) | 数值必须 ≤ 指定值(仅限整数) | |
@Min(value) | 数值必须 ≥ 指定值(仅限整数) | |
@Negative | 必须为负数 | |
@NegativeOrZero | 必须为负数或 0 | |
| 布尔检查 | @AssertTrue | 必须为 true |
@AssertFalse | 必须为 false | |
| 长度检查 | @Size(min,max) | 字符串/集合/数组长度在 [min,max] 范围内 |
| 日期检查 | @Future | 必须是将来日期 |
@FutureOrPresent | 必须是将来或当前日期 | |
@Past | 必须是过去日期 | |
@PastOrPresent | 必须是过去或当前日期 | |
| 其他检查 | @Email | 符合邮箱格式(可配置宽松模式) |
@Pattern(regexp) | 符合正则表达式 |
2.2 Hibernate Validator 扩展注解
| 分类 | 注解 | 说明 |
|---|---|---|
| 范围检查 | @Range(min,max) | 数值必须在 [min,max] 范围内(支持整型、BigDecimal) |
| 字符串检查 | @Length(min,max) | 字符串长度在 [min,max] 范围内 |
| 格式检查 | @URL | 合法 URL 格式(可指定协议/主机/端口等参数) |
| 安全校验 | @SafeHtml | 过滤危险 HTML 标签(防御 XSS 攻击) |
| 其他检查 | @LuhnCheck | 银行卡号校验(Luhn 算法) |
@CNPJ | 巴西企业税号校验 | |
@CPF | 巴西个人税号校验 |
2.3 @Valid vs @Validated
| 特性 | @Valid | @Validated |
|---|---|---|
| 分组校验 | ❌ 不支持 | ✅ 支持 |
| 嵌套校验 | ✅ 支持 | ❌ 不支持 |
| 校验触发 | 自动触发 | 需配合AOP使用 |
3. 快速入门
3.1 添加依赖
<dependencies><!-- 实现对 Spring MVC 的自动化配置 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 保证 Spring AOP 相关的依赖包 --><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId></dependency><!-- 方便等会写单元测试 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies>
- spring-boot-starter-web 依赖里,已经默认引入 hibernate-validator 依赖,所以本示例使用的是 Hibernate Validator 作为 Bean Validation 的实现框架。
3.2 DTO 对象示例
public class UserAddDTO {/*** 账号*/@NotEmpty(message = "登录账号不能为空")@Length(min = 5, max = 16, message = "账号长度为 5-16 位")@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")private String username;/*** 密码*/@NotEmpty(message = "密码不能为空")@Length(min = 4, max = 16, message = "密码长度为 4-16 位")private String password;// ... 省略 setting/getting 方法
}
3.3 启用校验
@RestController
@RequestMapping("/users")
@Validated
public class UserController {private Logger logger = LoggerFactory.getLogger(getClass());@GetMapping("/get")public void get(@RequestParam("id") @Min(value = 1L, message = "编号必须大于 0") Integer id) {logger.info("[get][id: {}]", id);}@PostMapping("/add")public void add(@Valid UserAddDTO addDTO) {logger.info("[add][addDTO: {}]", addDTO);}}
4. 统一异常处理
4.1 @Valid 的异常处理
当使用 @Valid 注解进行参数校验时,校验失败会抛出 MethodArgumentNotValidException
全局拦截示例:
@RestControllerAdvice
public class GlobalExceptionHandler {/**** 触发场景* 对象参数校验失败(如 @RequestBody + @Valid)* 常见使用组合* @Valid + DTO 对象* 校验注解适用对象* 对象属性级校验(@NotNull/@Size 等)*/@ExceptionHandler(value = MethodArgumentNotValidException.class)public Result handleValidException(MethodArgumentNotValidException e) {BindingResult bindingResult = e.getBindingResult();List<String> errors = bindingResult.getFieldErrors().stream().map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage()).collect(Collectors.toList());return Result.error(400, "参数校验失败", errors);}
}
4.2 @Validated 的异常处理
当使用 @Validated 注解时,需要分场景处理:
场景 1:Controller 层方法参数校验
如果直接在 Controller 方法参数上使用 @Validated 校验简单类型(如 @RequestParam、@PathVariable),校验失败会抛出 ConstraintViolationException。
全局拦截示例:
@RestControllerAdvice
public class GlobalExceptionHandler {/**** 触发场景* 方法参数直接校验* 常见使用组合* @Validated + 方法参数校验* 校验注解适用对象* 方法参数级校验(@RequestParam + @NotBlank 等)* 需要在类上标注 @Validated 才能触发 ConstraintViolationException*/@ExceptionHandler(ConstraintViolationException.class)public Result handleConstraintViolation(ConstraintViolationException e) {List<String> errors = e.getConstraintViolations().stream().map(v -> v.getPropertyPath() + ": " + v.getMessage()).collect(Collectors.toList());return Result.error(400, "参数校验失败", errors);}}
场景 2:校验对象参数
如果校验对象参数(如 @RequestBody),行为与 @Valid 一致,抛出 MethodArgumentNotValidException(处理方式同 @Valid)。
完整异常处理配置
@RestControllerAdvice
public class GlobalExceptionHandler {/**** 触发场景* 对象参数校验失败 如 (@RequestBody + @Valid/@RequestBody + @Validated)* 常见使用组合* (@Valid + DTO 对象/@Validated + DTO 对象)* 校验注解适用对象* 对象属性级校验(@NotNull/@Size 等)*/@ExceptionHandler(MethodArgumentNotValidException.class)public Result handleMethodArgumentNotValid(MethodArgumentNotValidException e) {BindingResult result = e.getBindingResult();List<String> errors = result.getFieldErrors().stream().map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage()).collect(Collectors.toList());return Result.error(400, "对象参数校验失败", errors);}/**** 触发场景* 方法参数直接校验* 常见使用组合* @Validated + 方法参数校验* 校验注解适用方法参数* 方法参数级校验(@RequestParam + @NotBlank 等)* 需要在类上标注 @Validated 才能触发 ConstraintViolationException*/@ExceptionHandler(ConstraintViolationException.class)public Result handleConstraintViolation(ConstraintViolationException e) {List<String> errors = e.getConstraintViolations().stream().map(v -> v.getPropertyPath() + ": " + v.getMessage()).collect(Collectors.toList());return Result.error(400, "简单参数校验失败", errors);}
}
4.3 关键总结
| 注解 | 使用场景 | 抛出异常 |
|---|---|---|
| @Valid | 校验对象参数(如 @RequestBody) | MethodArgumentNotValidException |
| @Validated | 校验简单类型参数(如 @RequestParam) | ConstraintViolationException |
| @Validated | 校验对象参数(需配合 @Valid 使用) | MethodArgumentNotValidException |
5. 自定义约束
在大多数项目中,无论是 Bean Validation 定义的约束,还是 Hibernate Validator 附加的约束,都是无法满足我们复杂的业务场景。所以,我们需要自定义约束。
开发自定义约束一共只要两步:
- 1)编写自定义约束的注解;
- 2)编写自定义的校验器 ConstraintValidator 。
下面,就让我们一起来实现一个自定义约束,用于校验参数必须在枚举值的范围内。
5.1 ArrayValuable
public interface ArrayValuable<T> {/*** @return 数组*/T[] array();
}
5.2 CommonStatusEnum
@Getter
@AllArgsConstructor
public enum CommonStatusEnum implements ArrayValuable<Integer> {ENABLE(0, "开启"),DISABLE(1, "关闭");public static final Integer[] ARRAYS = Arrays.stream(values()).map(CommonStatusEnum::getStatus).toArray(Integer[]::new);/*** 状态值*/private final Integer status;/*** 状态名*/private final String name;@Overridepublic Integer[] array() {return ARRAYS;}}
5.3 @InEnum
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.ANNOTATION_TYPE,ElementType.CONSTRUCTOR,ElementType.PARAMETER,ElementType.TYPE_USE
})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {InEnumValidator.class}
)
public @interface InEnum {/*** @return 实现 ArrayValuable 接口的类*/Class<? extends ArrayValuable<?>> value();String message() default "必须在指定范围 {value}";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};}
5.4 InEnumValidator
public class InEnumValidator implements ConstraintValidator<InEnum, Object> {private List<?> values;@Overridepublic void initialize(InEnum annotation) {ArrayValuable<?>[] values = annotation.value().getEnumConstants();if (values.length == 0) {this.values = Collections.emptyList();} else {this.values = Arrays.asList(values[0].array());}}@Overridepublic boolean isValid(Object value, ConstraintValidatorContext context) {// 为空时,默认不校验,即认为通过if (value == null) {return true;}// 校验通过if (values.contains(value)) {return true;}// 校验不通过,自定义提示语句context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate().replaceAll("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句return false;}}
5.5 UserUpdateStatusDTO
public class UserUpdateStatusDTO{/*** 用户编号*/@NotNull(message = "用户编号不能为空")private Integer id;/*** 状态*/@NotNull(message = "状态不能为空")@InEnum(value = CommonStatusEnum .class, message = "状态必须是 {value}")private Integer status;// ... 省略 set/get 方法
}
5.6 UserController
@PostMapping("/update_status")
public void updateStatus(@Valid UserUpdateStatusDTO updateStatusDTO) {logger.info("[updateStatus][updateStatusDTO: {}]", updateStatusDTO);
}
6. 分组校验
6.1 UserUpdateStatusDTO
public class UserUpdateStatusDTO {/*** 分组 01 ,要求状态必须为 true*/public interface Group01 {}/*** 状态 02 ,要求状态必须为 false*/public interface Group02 {}/*** 状态*/@AssertTrue(message = "状态必须为 true", groups = Group01.class)@AssertFalse(message = "状态必须为 false", groups = Group02.class)private Boolean status;// ... 省略 set/get 方法
}
6.2 UserController
@PostMapping("/update_status_true")
public void updateStatusTrue(@Validated(UserUpdateStatusDTO.Group01.class) UserUpdateStatusDTO updateStatusDTO) {logger.info("[updateStatusTrue][updateStatusDTO: {}]", updateStatusDTO);
}@PostMapping("/update_status_false")
public void updateStatusFalse(@Validated(UserUpdateStatusDTO.Group02.class) UserUpdateStatusDTO updateStatusDTO) {logger.info("[updateStatusFalse][updateStatusDTO: {}]", updateStatusDTO);
}
7. 手动触发校验
@Service
public class ManualValidateService {@Autowiredprivate Validator validator;public void validate(UserAddDTO addDTO) {Set<ConstraintViolation<UserAddDTO>> result = validator.validate(addDTO);// 打印校验结果 // <4>for (ConstraintViolation<UserAddDTO> constraintViolation : result) {// 属性:消息System.out.println(constraintViolation.getPropertyPath() + ":" + constraintViolation.getMessage());}}
}
掌握这些核心要点,你的 Spring Boot 参数校验体系将兼具 健壮性 与 可维护性!
相关文章:
Spring Boot 参数校验 Validation 终极指南
1. 概述 Spring Validation 基于 JSR-303(Bean Validation)规范,通过Validated注解实现声明式校验。核心优势: 零侵入性:基于 AOP 实现方法拦截校验规范统一:兼容 Bean Validation 标准注解功能扩展&…...
Policy Gradient思想、REINFORCE算法,以及贪吃蛇小游戏(一)
文章目录 Policy Gradient思想论文REINFORCE算法论文Policy Gradient思想和REINFORCE算法的关系用一句人话解释什么是REINFORCE算法策略这个东西实在是太抽象了,它可以是一个什么我们能实际感受到的东西?你说的这个我理解了,但这个东西,我怎么优化?在一堆函数中,找到最优…...
Angular 框架详解:从入门到进阶
Hi,我是布兰妮甜 !在当今快速发展的 Web 开发领域,Angular 作为 Google 主导的企业级前端框架,以其完整的解决方案、强大的类型系统和丰富的生态系统,成为构建大型复杂应用的首选。不同于其他渐进式框架,An…...
Profibus DP主站转modbusTCP网关与dp从站通讯案例
Profibus DP主站转modbusTCP网关与dp从站通讯案例 在当前工业自动化的浪潮中,不同协议之间的通讯转换成为了提升生产效率和实现设备互联的关键。Profibus DP作为一种广泛应用的现场总线技术,与Modbus TCP的结合,为工业自动化系统的集成带来了…...
快速部署大模型 Openwebui + Ollama + deepSeek-R1模型
背景 本文主要快速部署一个带有web可交互界面的大模型的应用,主要用于开发测试节点,其中涉及到的三个组件为 open-webui Ollama deepSeek开放平台 首先 Ollama 是一个开源的本地化大模型部署工具,提供与OpenAI兼容的Api接口,可以快速的运…...
Ethan独立开发产品日报 | 2025-04-15
1. Whatting 专属于你的iPad日记 还在Goodnotes里使用PDF模板吗?是时候告别到处翻找PDF的日子了——来试试Whatting吧!在Whatting中,你可以根据自己的喜好,灵活组合小部件,打造专属的日记布局。今天就免费开始吧&…...
H.265硬件视频编码器xk265代码阅读 - 帧内预测
源代码地址: https://github.com/openasic-org/xk265 帧内预测具体逻辑包含在代码xk265\rtl\rec\rec_intra\intra_pred.v 文件中。 module intra_pred() 看起来是每次计算某个4x4块的预测像素值。 以下代码用来算每个pred_angle的具体数值,每个mode_i对应…...
Arcgis经纬线标注设置(英文、刻度显示)
在arcgis软件中绘制地图边框,添加经纬度度时常常面临经纬度出现中文,如下图所示: 解决方法,设置一下Arcgis的语言 点击高级--确认 这样Arcgis就转为英文版了,此时在来看经纬线刻度的标注,自动变成英文...
MCP协议,.Net 使用示例
服务器端示例 基础服务器 以下是一个基础的 MCP 服务器示例,它使用标准输入输出(stdio)作为传输方式,并实现了一个简单的回显工具: using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.H…...
Windows安装Ollama并指定安装路径(默认C盘)
手打不易,如果转摘,请注明出处! 注明原文:http://blog.csdn.net/q258523454/article/details/147289192 一、下载Ollama 访问Ollama官网 打开浏览器,访问Ollama的官方网站:https://ollama.ai/。 在官网首页…...
微信小程序中大型项目开发实战指南
🌐从架构设计到性能优化:微信小程序中大型项目开发实战指南 本文将深入探讨微信小程序在中大型项目开发中的架构设计、组件化方案、状态管理、性能优化策略、网络请求封装等核心内容,帮助你构建高质量、可维护、易扩展的小程序工程。 &#x…...
读《思考的框架有感》
书名 :《思考的框架》一沙恩.帕里什 汉隆剃刀定律目前已经难以溯源。它指的是,能解释为愚蠢的,就不要解释为恶意。在复杂的世界中,使用这一模型有助于我们避免妄想和偏执。如果我们拒绝假定一切糟糕的结果都是坏人的错…...
Python自动化处理奖金分摊:基于连续空值的智能分配算法升级
Python自动化处理奖金分摊:基于连续空值的智能分配算法升级 原创 IT小本本 IT小本本 2025年04月04日 02:00 北京 引言 在企业薪酬管理中,团队奖金分配常涉及复杂的分摊规则。传统手工分摊不仅效率低下,还容易因人为疏漏导致分配不公。 本文…...
AI工具箱源码+成品网站源码+springboot+vue
大家好,今天给大家分享一个靠AI广告赚钱的项目:AI工具箱成品网站源码,源码支持二开,但不允许转售!! 本人专门为小型企业和个人提供的解决方案。 不懂技术的也可以直接部署工具箱网站,成为站长&…...
centos7停服yum更新kernel失败解决办法
yum更新kernel均失败 由于centos停服,使用yum源安装内核失败 # rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org# yum -y install https://www.elrepo.org/elrepo-release-7.0-4.el7.elrepo.noarch.rpm Loaded plugins: fastestmirror elrepo-release…...
如何下载免费地图数据?
按照以下步骤下载免费地图数据。 1、安装GIS地图下载器 从GeoSaaS(.COM)官网下载“GIS地图下载器”软件:,安装完成后桌面上出现”GIS地图下载器“图标。 双击桌面图标打开”GIS地图下载器“ 2、下载地图数据 点击主界面底部的“…...
IO 口作为外部中断输入
外部中断 1. NVIC2. EXTI 1. NVIC NVIC即嵌套向量中断控制器,它是内核的器件,M3/M4/M7 内核都是支持 256 个中断,其中包含了 16 个系统中断和 240 个外部中断,并且具有 256 级的可编程中断设置。然而芯片厂商一般不会把内核的这些…...
Go 语言实现的简单 CMS Web
Go 语言实现的简单 CMS Web 以下是一个使用 Go 语言实现的简单 CMS Web 演示代码示例, 包含基本的内容管理功能: 项目结构 ### 项目结构 cms-demo/ ├── main.go ├── handlers/ ├── models/ ├── views/ │ ├── home.html │ ├─…...
《MySQL基础:了解MySQL周边概念》
1.登录选项的认识 -h:指明登录部署了mysql服务的主机,默认为127.0.0.1-P:指明要访问的端口号,默认为3306-u:指明登录用户-p:指明登录密码 2.什么是数据库 2.1认识数据库 第一点理解。 mysql是数据库的客户…...
Spring boot 知识整理
一、SpringBoot 背景内容梳理 SpringBoot是一个基于Spring框架的开源框架,用于简化Spring应用程序的初始搭建和开发过程。它通过提供约定优于配置的方式,尽可能减少开发者的工作量,使得开发Spring应用变得更加快速、便捷和高效。 SpringBoot…...
transformer 规范化层
目标 了解规范化层的作用掌握规范化层的实现过程 作用 所有的深层网络模型都需要标准网络层, 因为随着网络层数量的增加, 通过多层的计算后参数可能出现过大或者过小的情况, 这样可能导致在学习过程出现异常, 模型可能收敛比较慢,因此都会在一定的层数后接规范化层进行数值的…...
RCL谐振电压增益曲线
谐振电路如何通过调频实现稳压? 为什么要做谐振? 在谐振状态实现ZVS导通,小电流关断 电压增益GVo/Vin,相当于产出投入比 当ff0时,G1时,输出电压输入电压 当G<1时,输出电压<输入电压 …...
JavaScript:表单及正则表达式验证
今天我要介绍的是在JavaScript中关于表单验证内容的知识点介绍: 关于表单验证,我接下来则直接将内容以及效果显示出来并作注解,这样可以清晰看见这个表达验证的妙用: <form id"ff" action"https://www.baidu.…...
一、Appium环境安装
找了一圈操作手机的工具或软件,踩了好多坑,最后决定用这个工具(影刀RPA手机用的也是这个),目前最新的版本是v2.17.1,是基于nodejs环境的,有两种方式,我只试了第一种方式,第二种方式应该是比较简…...
精益数据分析(3/126):用数据驱动企业发展的深度解析
精益数据分析(3/126):用数据驱动企业发展的深度解析 大家好!一直以来,我都坚信在当今竞争激烈的商业环境中,数据是企业获得竞争优势的关键。最近深入研究《精益数据分析》这本书,收获颇丰&…...
暂存一下等会写
#include<easyx.h> IMAGE SNOW 图形变量 struct MOVE生存结构体 {int x0;int y0; bool livefalse;}; initgraph(800, 800);初始化图形界面 MOVE snowflake[5000];目标数量 loadimage(&SNOW, "snow.png");加载图片 BeginBatchDraw(); 开始批量绘图。…...
【c++深入系列】:new和delete运算符详解
🔥 本文专栏:c 🌸作者主页:努力努力再努力wz 💪 今日博客励志语录: “生活不会向你许诺什么,尤其不会向你许诺成功。它只会给你挣扎、痛苦和煎熬的过程。但只要你坚持下去,终有一天&…...
正弦波有效值和平均值(学习笔记)
一个周期的正弦波在坐标轴上围的面积有多大? 一般正弦波以 y Asin(wx)表示,其中A为振幅,W为角速度。周期T 2π/w; 确定积分区间是x 0,到x 2π。 计算绝对值积分: 变量代还:wx θ,dx dθ…...
《分布式软总线架构下,设备虚拟化技术的深度剖析与优化策略》
设备之间的互联互通和协同工作已成为一种趋势。分布式软总线架构作为实现这一目标的关键技术,为不同设备之间的通信和协作提供了基础。而设备虚拟化技术则是在分布式软总线架构下,进一步提升设备资源利用效率的重要手段。本文将深入探讨在分布式软总线架…...
首次打蓝桥杯总结(c/c++B组)
目录 一、对每个题进行总结 1.填空题 2.第一个大题---可分解的正整数(10--3) 3.第二道大题---产值调整(10--3) 4.第三道大题---画展部署(15--7) 5.第四道大题---水质检测(15--3&#x…...
