SpringBoot参数校验:@Valid 与 @Validated 详解
SpringBoot参数校验:@Valid 与 @Validated 详解
一、案例(参数校验的必要性)
传统方式(无注解)的缺点:
// 需要手动校验每个字段,代码冗余且易出错
public String register(User user) {// 手动校验每个字段if (user.getEmail() == null || !isValidEmail(user.getEmail())) {throw new IllegalArgumentException("邮箱格式错误");}if (user.getPassword().length() < 8) {throw new IllegalArgumentException("密码长度需≥8位");}// 校验逻辑与业务代码耦合,难以复用
}
问题总结:
- 代码冗余:相同校验逻辑重复编写
- 维护困难:校验规则分散,修改成本高
- 可读性差:业务逻辑被大量
if-else淹没
注解方式的优势:
public class User {@Email(message = "邮箱格式不合法") // 一行注解替代复杂校验private String email;@Size(min = 8, message = "密码长度需≥8位")private String password;
}@PostMapping("/register")
public String register(@Valid @RequestBody User user) { // 校验逻辑由框架自动处理return "success";
}
核心优势:
- 声明式校验:通过注解自动完成参数验证
- 代码简洁:减少冗余的
if-else判断 - 统一规范:标准化校验规则,降低维护成本
二、@Valid 注解
简介
- Java标准注解(javax.validation)
- 用于触发 Bean Validation 校验机制
- 可校验方法参数和成员属性
SpringBoot配置
<!-- pom.xml 必须依赖 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency>
使用示例
public class User {@NotNull(message = "用户名不能为空")private String name;@Min(value = 18, message = "年龄必须≥18岁")private Integer age;
}@PostMapping("/users")
public String createUser(@Valid @RequestBody User user) { // 触发校验return "success";
}
全局异常处理(核心实践)
@RestControllerAdvice
public class GlobalExceptionHandler {// 处理@Valid/@Validated抛出的校验异常@ExceptionHandler(MethodArgumentNotValidException.class)public ResponseEntity<Map<String, String>> handleValidationException(MethodArgumentNotValidException ex) {Map<String, String> errors = new HashMap<>();ex.getBindingResult().getFieldErrors().forEach(error -> errors.put(error.getField(), error.getDefaultMessage()));return ResponseEntity.badRequest().body(errors);}
}
效果:自动返回结构化错误信息(字段名+错误描述)
三、@Validated 注解
简介
- Spring框架注解(org.springframework.validation.annotation)(@Validated是Spring框架提供的,导入springboot的依赖就会自动导入这个依赖)
- 支持分组校验(Group Validation)
- 可标注在类、方法、参数上
核心功能
// 分组接口定义
public interface CreateGroup {}
public interface UpdateGroup {}public class User {@NotBlank(groups = CreateGroup.class)private String name;@NotNull(groups = {CreateGroup.class, UpdateGroup.class})private Integer age;
}// 使用分组校验
@PostMapping("/users")
public String createUser(@Validated(CreateGroup.class) @RequestBody User user) {return "success";
}
四、常用校验注解
最常用的注解:
| 注解 | 说明 | 示例 |
|---|---|---|
@NotNull | 值不能为null | @NotNull(message="字段必填") |
@NotBlank | 字符串非空(trim后) | 适用于用户名、密码等 |
@Min/@Max | 数值范围限制 | @Min(18) |
@Pattern | 正则表达式校验 | @Pattern(regexp="^1[3-9]\\d{9}$") |
@Valid | 嵌套对象校验 | 用于对象内的子对象属性 |
比较常用的注解:
| 注解 | 适用类型 | 说明 | 示例 |
|---|---|---|---|
@Future | Date | 日期必须在未来 | @Future(message="截止时间无效") |
@Digits | 数值类型 | 整数位和小数位限制 | @Digits(integer=3, fraction=2) |
@Negative | 数值类型 | 必须为负数 | 常用于财务系统 |
@NotEmpty | 集合/字符串 | 集合非空或字符串长度>0 | 比@NotBlank更宽松 |
组合注解(自定义)
// 自定义手机号校验注解
@Documented
@Constraint(validatedBy = PhoneValidator.class)
@Target({FIELD, PARAMETER})
@Retention(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 PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {return value != null && PHONE_PATTERN.matcher(value).matches();}
}
使用场景:复用企业特定校验规则(如公司内部员工编号)
五、@Valid 与 @Validated 区别
| 特性 | @Valid | @Validated |
|---|---|---|
| 标准规范 | JSR-303/JSR-349 标准 | Spring 框架扩展 |
| 分组校验 | 不支持 | 支持 |
| 嵌套校验 | 需要显式添加@Valid | 不自动支持嵌套校验 |
| 作用位置 | 方法参数、成员属性 | 类、方法、参数 |
六.高频问题与最佳实践
常见问题排查
- 注解不生效:
- 检查是否忘记添加
@Valid/@Validated - 确保校验对象未被
@RequestBody等注解错误包裹
- 检查是否忘记添加
- 嵌套校验失败:
- 确认在嵌套对象属性上添加了
@Valid
- 确认在嵌套对象属性上添加了
性能优化建议
- 避免过度校验:在Controller层做基础校验,复杂逻辑放到Service层
- 使用分组校验:减少不必要的校验开销
高级技巧
// 动态分组校验(根据请求参数决定校验组)
@Validated
@RestController
public class UserController {@PostMapping("/users")public String createUser(@RequestParam String type,@Validated({Default.class, type.equals("vip") ? VipGroup.class : Default.class}) @RequestBody User user) {return "success";}
}
实现原理:利用Spring EL表达式动态选择校验组
七、重点总结
- 优先使用场景:
- 优先使用
@Valid的场景:嵌套对象校验、与非Spring框架整合 - 优先使用
@Validated的场景:需要分组校验、校验Service层方法参数
- 优先使用
- 配置要点:
- 要想使用
@Valid必须添加spring-boot-starter-validation依赖 - 校验失败会抛出
MethodArgumentNotValidException
- 要想使用
- 最佳实践:
// 嵌套校验示例
public class Order {@Valid // 必须显式添加private User user;
}// 分组校验示例
@Validated(UpdateGroup.class)
public void updateUser(@RequestBody User user) {}
4.校验流程:
请求参数 → 注解声明 → 自动校验 → 异常处理(@ControllerAdvice)
5.相较传统参数校验优势:
- 减少70%以上的参数校验代码量
- 通过统一异常处理实现错误响应的标准化
- 提升代码可维护性和团队协作效率
相关文章:
SpringBoot参数校验:@Valid 与 @Validated 详解
SpringBoot参数校验:Valid 与 Validated 详解 一、案例(参数校验的必要性) 传统方式(无注解)的缺点: // 需要手动校验每个字段,代码冗余且易出错 public String register(User user) {// 手动…...
<论文>MiniCPM:利用可扩展训练策略揭示小型语言模型的潜力
一、摘要 本文跟大家一起阅读的是清华大学的论文《MiniCPM: Unveiling the Potential of Small Language Models with Scalable Training Strategies》 摘要: 对具有高达万亿参数的大型语言模型(LLMs)的兴趣日益增长,但同时也引发…...
SpringCloud系列教程(十三):Sentinel流量控制
SpringCloud中的注册、发现、网关、服务调用都已经完成了,现在就剩下最后一部分,就是关于网络控制。SpringCloud Alibaba这一套中间件做的非常好,把平时常用的功能都集成进来了,而且非常简单高效。我们下一步就完成最后一块拼图Se…...
Codeforces Round 502 E. The Supersonic Rocket 凸包、kmp
题目链接 题目大意 平面上给定两个点集,判定两个点集分别形成的凸多边形能否通过旋转、平移重合。 点集大小 ≤ \leq ≤ 1 0 5 10^{5} 105,坐标范围 [0, 1 0 8 10^{8} 108 ]. 思路 题意很明显,先求出凸包再判断两凸包是否同构。这里用…...
论文阅读方法
文章目录 步骤一:对论文进行自我判断阅读题目和关键词。阅读摘要阅读总结要点 步骤二:阅读文章阅读图表和图表的注释阅读引言阅读实验部分阅读结果和作者对结果的讨论(创新点)要点 步骤三:精度论文回答问题1回答问题2回…...
ArcGIS操作:15 计算点的经纬度,并添加到属性表
注意:需要转化为地理坐标系 1、打开属性表,添加字段 2、计算字段(以计算纬度为例 !Shape!.centroid.Y ) 3、效果...
蓝桥杯历年真题题解
1.轨道炮(数学模拟) #include <iostream> #include <map> using namespace std; const int N1010; int x[N],y[N],v[N]; char d[N]; int main() {int n;int ans-100;cin>>n;for(int i1;i<n;i)cin>>x[i]>>y[i]>>v…...
IP-地址
主机号(Host ID) IP地址简介:IP地址是每台接入互联网的设备所拥有的唯一标识符,类似于电话号码的分层结构,由网络号和主机号组成。为了便于记忆,32位二进制的IP地址通常以点分十进制表示。 网络号…...
MoonSharp 文档一
目录 1.Getting Started 步骤1:在 IDE 中引入 MoonSharp 步骤2:引入命名空间 步骤3:调用脚本 步骤4:运行代码 2.Keeping a Script around 步骤1:复现前教程所有操作 步骤2:改为创建Script对象 步骤…...
2025-03-08 学习记录--C/C++-PTA 习题10-1 判断满足条件的三位数
合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。💪🏻 一、题目描述 ⭐️ 裁判测试程序样例: #include <stdio.h> #include <math.h>int search( int n );int…...
三星首款三折叠手机被曝外屏6.49英寸:折叠屏领域的新突破
在智能手机的发展历程中,折叠屏手机的出现无疑是一次具有里程碑意义的创新。它打破了传统手机屏幕尺寸的限制,为用户带来了更加多元和便捷的使用体验。而三星,作为手机行业的巨头,一直以来都在折叠屏技术领域积极探索和创新。近日,三星首款三折叠手机的诸多细节被曝光,其…...
大白话Vue Router 中路由守卫(全局守卫、路由独享守卫、组件内守卫)的种类及应用场景
大白话Vue Router 中路由守卫(全局守卫、路由独享守卫、组件内守卫)的种类及应用场景 答题思路 明确要介绍的内容:需要分别介绍 Vue Router 中全局守卫、路由独享守卫和组件内守卫这三种路由守卫的种类,详细说明它们的定义、使用…...
CUDA编程之OpenCV与CUDA结合使用
OpenCV与CUDA的结合使用可显著提升图像处理性能。 一、版本匹配与环境配置 CUDA与OpenCV版本兼容性 OpenCV各版本对CUDA的支持存在差异,例如OpenCV 4.5.4需搭配CUDA 10.02,而较新的OpenCV 4.8.0需使用更高版本CUDA。 需注意部分模块(…...
Educational Codeforces Round 7 F. The Sum of the k-th Powers 多项式、拉格朗日插值
题目链接 题目大意 求 ( ∑ i 1 n i k ) (\sum_{i1}^{n} i^k) (∑i1nik) m o d ( 1 0 9 7 ) mod(10^97) mod(1097) . 数据范围 : 1 ≤ n ≤ 1 0 9 1 \leq n \leq 10^9 1≤n≤109 , 0 ≤ k ≤ 1 0 6 0 \leq k \leq 10^6 0≤k≤106 . 思路 令 f ( n ) ∑ …...
LINUX网络基础 [五] - HTTP协议
目录 HTTP协议 预备知识 认识 URL 认识 urlencode 和 urldecode HTTP协议格式 HTTP请求协议格式 HTTP响应协议格式 HTTP的方法 HTTP的状态码 编辑HTTP常见Header HTTP实现代码 HttpServer.hpp HttpServer.cpp Socket.hpp log.hpp Makefile Web根目录 H…...
WPS Word中英文混杂空格和行间距不一致调整方案
文章目录 问题1:在两端对齐的情况下,如何删除参考文献(英文)的空格问题2:中英文混杂行间距不一致问题问题3:设置中文为固定字体,设置西文为固定字体参考 问题1:在两端对齐的情况下&a…...
C++ Qt创建计时器
在Qt中,可以使用QTimer来创建一个简单的计时器。QTimer是一个用于定时触发事件的类,通常与QObject的子类(如QWidget)一起使用。以下是一个完整的示例,展示如何使用Qt创建一个带有计时器的窗口应用程序。 示例ÿ…...
CSDN博客:Markdown编辑语法教程总结教程(中)
❤个人主页:折枝寄北的博客 Markdown编辑语法教程总结 前言1. 列表1.1 无序列表1.2 有序列表1.3 待办事项列表1.4 自定义列表 2. 图片2.1 直接插入图片2.2 插入带尺寸的图片2.3 插入宽度确定,高度等比例的图片2.4 插入高度确定宽度等比例的图片2.5 插入居…...
nlp培训重点-5
1. LoRA微调 loader: # -*- coding: utf-8 -*-import json import re import os import torch import numpy as np from torch.utils.data import Dataset, DataLoader from transformers import BertTokenizer """ 数据加载 """cl…...
电子学会—2024年月6青少年软件编程(图形化)四级等级考试真题——水仙花数
水仙花数 如果一个三位数等于它各个数位上的数字的立方和,那么这个数就是水仙花数,例如:153 111 555 333,153就是一个水仙花数。 1.准备工作 (1)保留默认角色小猫; (2)白色背景。 2.功能实现 (1)使用循环遍历所有三位数,把所…...
若依分页的逻辑分析
看了一些网上的感觉都是 听君一席话, 如听一席话. 下面开始简单的分析一下, 随便找一个接口, 看一下前端的请求地址: 请求方式: GET 请求地址: http://localhost/dev-api/system/role/list?pageNum1&pageSize10 后端接口: PreAuthorize("ss.hasPermi(system:role:li…...
JetBrains学生申请
目录 JetBrains学生免费授权申请 IDEA安装与使用 第一个JAVA代码 1.利用txt文件和cmd命令运行 2.使用IDEA新建项目 JetBrains学生免费授权申请 本教程采用学生校园邮箱申请,所以要先去自己的学校申请校园邮箱。 进入JetBrains官网 点击立即申请,然…...
【算法方法总结·五】链表操作的一些技巧和注意事项
【算法方法总结五】链表操作的一些技巧和注意事项 【算法方法总结一】二分法的一些技巧和注意事项【算法方法总结二】双指针的一些技巧和注意事项【算法方法总结三】滑动窗口的一些技巧和注意事项【算法方法总结四】字符串操作的一些技巧和注意事项【算法方法总结五】链表操作…...
langchain系列(终)- LangGraph 多智能体详解
目录 一、导读 二、概念原理 1、智能体 2、多智能体 3、智能体弊端 4、多智能体优点 5、多智能体架构 6、交接(Handoffs) 7、架构说明 (1)网络 (2)监督者 (3)监督者&…...
侯捷 C++ 课程学习笔记:深入理解智能指针
文章目录 每日一句正能量一、引言二、智能指针的核心概念(一)std::unique_ptr(二)std::shared_ptr(三)std::weak_ptr 三、学习心得四、实际应用案例五、总结 每日一句正能量 如果说幸福是一个悖论ÿ…...
访问不了 https://raw.githubusercontent.com 怎么办?
修改 Hosts 文件(推荐) 原理:通过手动指定域名对应的 IP 地址,绕过 DNS 污染。 步骤: 1、访问 IPAddress.com,搜索 raw.githubusercontent.com,获取当前最新的 IPv4 地址(例如 1…...
大模型工程师学习日记(十五):Hugging Face 模型微调训练(基于 BERT 的中文评价情感分析)
1. datasets 库核心方法 1.1. 列出数据集 使用 d atasets 库,你可以轻松列出所有 Hugging Face 平台上的数据集: from datasets import list_datasets# 列出所有数据集 all_datasets list_datasets()print(all_datasets)1.2. 加载数据集 你可以通过 l…...
Codeforces Round 258 (Div. 2) E. Devu and Flowers 生成函数
题目链接 题目大意 有 n n n ( 1 ≤ n ≤ 20 ) (1\leq n \leq 20) (1≤n≤20) 个花瓶,第 i i i 个花瓶里有 f i f_i fi ( 1 ≤ f i ≤ 1 0 12 ) (1\leq f_i \leq 10^{12}) (1≤fi≤1012) 朵花。现在要选择 s s s ( 1 ≤ s ≤ 1 0 14 ) (1\leq s \leq 1…...
MySQL-----SELECT语句-查询
目录 SELECT语句-查询 1.格式 2.操作 3.算数表达式 SELECT语句-查询 1.格式 📖简单查询: 格式: select 字段1,字段n from 表名; 起别名: 通过在字段后添加 as 别名 as可以省略 改变表头 eg: select username "用户名",password as "…...
子数组、子串系列(典型算法思想)—— OJ例题算法解析思路
一、53. 最大子数组和 - 力扣(LeetCode) 算法代码: class Solution { public:int maxSubArray(vector<int>& nums) {// 1. 创建 dp 表// dp[i] 表示以第 i 个元素结尾的子数组的最大和int n nums.size();vector<int> dp(n…...
