Spring Validation中9个数据校验工具

Spring Validation作为Spring生态系统的重要组成部分,提供了一套强大而灵活的数据校验机制。

1. Bean Validation基础注解

Spring Validation集成了JSR-380 (Bean Validation 2.0)规范,提供了一系列开箱即用的校验注解。

常用注解示例

@Data
public class UserDTO {@NotNull(message = "用户ID不能为空")private Long id;@NotBlank(message = "用户名不能为空")@Size(min = 4, max = 20, message = "用户名长度必须在4到20个字符之间")private String username;@Email(message = "邮箱格式不正确")private String email;@Min(value = 18, message = "年龄必须大于或等于18")@Max(value = 120, message = "年龄必须小于或等于120")private Integer age;@Past(message = "出生日期必须是过去的日期")private LocalDate birthDate;@Pattern(regexp = "^1[3-9]\d{9}$", message = "手机号码格式不正确")private String phoneNumber;
}

在控制器中应用

@RestController
@RequestMapping("/api/users")
public class UserController {@PostMappingpublic ResponseEntity<UserDTO> createUser(@RequestBody @Valid UserDTO userDTO, BindingResult bindingResult) {if (bindingResult.hasErrors()) {// 处理验证错误throw new ValidationException(bindingResult);}// 处理业务逻辑return ResponseEntity.ok(userDTO);}
}

最佳实践:使用有意义的错误消息,保持一致的命名风格,避免在实体类上直接使用验证注解,而是在DTO对象上应用验证规则。

2. 自定义约束验证器

Spring Validation允许开发者创建自定义约束,满足特定业务规则的验证需求。

定义自定义约束注解

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueUsernameValidator.class)
public @interface UniqueUsername {String message() default "用户名已存在";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}

实现验证器

public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {@Autowiredprivate UserRepository userRepository;@Overridepublic boolean isValid(String username, ConstraintValidatorContext context) {if (username == null) {return true;  // 让@NotNull处理空值}return !userRepository.existsByUsername(username);}
}

应用自定义约束

public class UserRegistrationDTO {@NotBlank@Size(min = 4, max = 20)@UniqueUsernameprivate String username;// 其他字段...
}

使用场景:验证业务特定规则,如唯一性约束、密码复杂度、信用卡格式等。

3. 分组验证

分组验证允许根据不同场景应用不同的验证规则,例如创建和更新操作可能需要不同的验证逻辑。

定义验证分组

// 定义验证分组接口
public interface ValidationGroups {interface Create {}interface Update {}
}

应用分组到约束

@Data
public class ProductDTO {@Null(groups = ValidationGroups.Create.class, message = "创建产品时ID必须为空")@NotNull(groups = ValidationGroups.Update.class, message = "更新产品时ID不能为空")private Long id;@NotBlank(groups = {ValidationGroups.Create.class, ValidationGroups.Update.class})private String name;@PositiveOrZero(groups = ValidationGroups.Create.class)@Positive(groups = ValidationGroups.Update.class)private BigDecimal price;
}

在控制器中指定分组

@RestController
@RequestMapping("/api/products")
public class ProductController {@PostMappingpublic ResponseEntity<ProductDTO> createProduct(@RequestBody @Validated(ValidationGroups.Create.class) ProductDTO productDTO) {// 创建产品逻辑return ResponseEntity.ok(productDTO);}@PutMapping("/{id}")public ResponseEntity<ProductDTO> updateProduct(@PathVariable Long id,@RequestBody @Validated(ValidationGroups.Update.class) ProductDTO productDTO) {// 更新产品逻辑return ResponseEntity.ok(productDTO);}
}

提示:注意使用@Validated注解而不是@Valid,因为只有前者支持分组验证。

4. 嵌套验证

嵌套验证允许验证复杂对象结构中的嵌套对象。

定义嵌套对象

@Data
public class OrderDTO {@NotNullprivate Long id;@NotNull@Valid  // 标记需要级联验证的字段private CustomerDTO customer;@NotEmpty@Valid  // 验证集合中的每个元素private List<OrderItemDTO> items;
}@Data
public class CustomerDTO {@NotNullprivate Long id;@NotBlankprivate String name;@Emailprivate String email;@Valid  // 进一步嵌套验证private AddressDTO address;
}

关键点:在需要级联验证的字段上添加@Valid注解,确保验证深入到嵌套对象中。

5. 方法级别验证

Spring Validation不仅可以用于控制器参数,还可以应用于服务层的方法。

启用方法级别验证

@Configuration
@EnableMethodValidation
public class ValidationConfig {// 配置内容
}

定义带验证的服务方法

@Service
public class UserService {@Validatedpublic User createUser(@Valid UserDTO userDTO) {// 业务逻辑return new User();}@NotNullpublic User findById(@Min(1) Long id) {// 查询逻辑return new User();}@Validated(ValidationGroups.Update.class)public void updateUser(@Valid UserDTO userDTO) {// 更新逻辑}
}

应用场景:确保服务层方法接收到的参数和返回的结果符合预期,增强代码的健壮性。

6. 错误消息处理和国际化

Spring Validation提供了强大的错误消息处理和国际化支持。

自定义错误消息

ValidationMessages.properties文件中定义:

# ValidationMessages.properties
javax.validation.constraints.NotEmpty.message=字段不能为空
javax.validation.constraints.Email.message=不是有效的电子邮箱地址
user.name.size=用户名长度必须在{min}到{max}个字符之间

国际化错误消息

创建特定语言的属性文件:

# ValidationMessages_en.properties
javax.validation.constraints.NotEmpty.message=Field cannot be empty
javax.validation.constraints.Email.message=Not a valid email address
user.name.size=Username must be between {min} and {max} characters# ValidationMessages_zh_CN.properties
javax.validation.constraints.NotEmpty.message=字段不能为空
javax.validation.constraints.Email.message=不是有效的电子邮箱地址
user.name.size=用户名长度必须在{min}到{max}个字符之间

使用自定义消息

@Size(min = 4, max = 20, message = "{user.name.size}")
private String username;

7. 程序化验证

除了注解驱动的验证,Spring Validation还支持以编程方式进行验证。

使用Validator手动验证对象

@Service
public class ValidationService {private final Validator validator;public ValidationService(Validator validator) {this.validator = validator;}public <T> void validate(T object) {Set<ConstraintViolation<T>> violations = validator.validate(object);if (!violations.isEmpty()) {throw new ConstraintViolationException(violations);}}public <T> void validateWithGroup(T object, Class<?>... groups) {Set<ConstraintViolation<T>> violations = validator.validate(object, groups);if (!violations.isEmpty()) {throw new ConstraintViolationException(violations);}}public <T> List<String> getValidationErrors(T object) {return validator.validate(object).stream().map(ConstraintViolation::getMessage).collect(Collectors.toList());}
}

使用场景:在复杂业务逻辑中需要条件性验证,或者验证非控制器传入的对象时。

8. 组合约束

组合约束允许将多个基本约束组合成一个更复杂的约束,减少代码重复。

创建组合约束

@NotNull
@Size(min = 8, max = 30)
@Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=]).*$", message = "密码必须包含至少一个数字、小写字母、大写字母和特殊字符")
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {})
public @interface StrongPassword {String message() default "密码不符合安全要求";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}

应用组合约束

public class PasswordChangeDTO {@NotBlankprivate String oldPassword;@StrongPasswordprivate String newPassword;@NotBlankprivate String confirmPassword;
}

优点:提高代码可读性和可维护性,确保验证规则在整个应用中保持一致。

9. 跨字段验证

跨字段验证允许根据多个字段之间的关系进行验证。

创建类级别约束

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PasswordMatchesValidator.class)
public @interface PasswordMatches {String message() default "确认密码与新密码不匹配";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};String field();String fieldMatch();
}

实现验证器

public class PasswordMatchesValidator implements ConstraintValidator<PasswordMatches, Object> {private String field;private String fieldMatch;@Overridepublic void initialize(PasswordMatches constraintAnnotation) {this.field = constraintAnnotation.field();this.fieldMatch = constraintAnnotation.fieldMatch();}@Overridepublic boolean isValid(Object value, ConstraintValidatorContext context) {try {Object fieldValue = BeanUtils.getPropertyDescriptor(value.getClass(), field).getReadMethod().invoke(value);Object fieldMatchValue = BeanUtils.getPropertyDescriptor(value.getClass(), fieldMatch).getReadMethod().invoke(value);return (fieldValue != null) && fieldValue.equals(fieldMatchValue);} catch (Exception e) {return false;}}
}

应用类级别约束

@Data
@PasswordMatches(field = "newPassword", fieldMatch = "confirmPassword")
public class PasswordChangeDTO {@NotBlankprivate String oldPassword;@StrongPasswordprivate String newPassword;@NotBlankprivate String confirmPassword;
}

使用场景:验证密码确认、日期范围比较、最小/最大值比较等。

总结

Spring Validation提供了一套全面而强大的数据校验工具,从基本的注解验证到复杂的自定义约束,从单一字段验证到跨字段关系验证,都有相应的解决方案。

合理利用这些验证工具,不仅能提高应用的健壮性和安全性,还能改善代码质量和可维护性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/82802.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/82802.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI 搜索引擎 MindSearch

背景 RAG是一种利用文档减少大模型的幻觉&#xff0c;AI搜索也是 AI 搜索引擎 MindSearch 是一个开源的 AI 搜索引擎框架&#xff0c;具有与 Perplexity.ai Pro 相同的性能。您可以轻松部署它来构建您自己的搜索引擎&#xff0c;可以使用闭源 LLM&#xff08;如 GPT、Claude…

Java高频面试之并发编程-16

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本baby今天又来报道了&#xff01;哈哈哈哈哈嗝&#x1f436; 面试官&#xff1a;volatile 实现原理是什么&#xff1f; volatile 关键字的实现原理 volatile 是 Java 中用于解决多线程环境下变量可见性…

《零基础学机器学习》学习大纲

《零基础学机器学习》学习大纲 《零基础学机器学习》采用对话体的形式&#xff0c;通过人物对话和故事讲解机器学习知识&#xff0c;使内容生动有趣、通俗易懂&#xff0c;降低了学习门槛&#xff0c;豆瓣高分9.1分&#xff0c;作者权威。 接下来的数篇文章&#xff0c;我将用…

C# 中 static的使用

静态(static)是C#中一个重要的关键字&#xff0c;它可以应用于类、方法、属性和字段。 静态类 静态类的特点&#xff1a; 不能实例化只能包含静态成员密封的&#xff08;sealed&#xff09;,不能被继承 应用场景&#xff1a; 工具类/辅助类数学计算类&#xff1a;如Math类…

C++蓝桥杯真题(题目+解析+流程图)(特殊运算符+四叶玫瑰数+质因数的个数+最大的矩形纸片+数字游戏+活动人数)

C++蓝桥杯真题 蓝桥杯省赛C++题目分析1. 特殊运算符题目描述输入描述输出描述输入输出样例正确代码错误代码分析流程图2. 四叶玫瑰数题目描述输入描述输出描述输入输出样例正确代码错误代码分析流程图3. 质因数的个数题目描述输入描述输出描述输入输出样例正确代码错误代码分析…

MYSQL 索引与数据结构笔记

MYSQL 索引与数据结构笔记 文章目录 MYSQL 索引与数据结构笔记1. B-Tree 与 B Tree 基础对比一、B 树的优势二、B 树的进一步优化三、综合对比结论 2. MySQL 为何选择 B Tree3. 索引使用示例与性能分析3.1 整数字段索引查询3.2 字符字段索引查询 4. 索引失效与类型转换陷阱5. 小…

电路中的DGND、GROUND、GROUND_REF的区别,VREF、VCC、VDD、VEE和VSS的区别?

目录 1 DGND、GROUND、GROUND_REF的区别 1.1 DGND&#xff08;Digital Ground&#xff09; 1.2 GROUND&#xff08;Ground&#xff09; 1.3 GROUND_REF&#xff08;Ground Reference&#xff09; 1.4 区别 2 VREF、VCC、VDD、VEE和VSS的区别 2.1 VREF&#xff08;Refere…

OpenHarmony平台驱动开发(十),MMC

OpenHarmony平台驱动开发&#xff08;十&#xff09; MMC 概述 功能简介 MMC&#xff08;MultiMedia Card&#xff09;即多媒体卡&#xff0c;是一种用于固态非易失性存储的小体积大容量的快闪存储卡。 MMC后续泛指一个接口协定&#xff08;一种卡式&#xff09;&#xff0…

C++ 的 VS 项目中引入跨平台包管理工具 conan

我们知道 C 不像很多其他语言有包管理工具&#xff0c;比如 Python 有 pip&#xff0c;Java 有 maven&#xff0c;C# 有 nuget&#xff0c;JS 有 npm&#xff0c;Go 有 go mod&#xff0c;Rust 有 cargo&#xff0c;项目中需要自己手动引入第三方库&#xff0c;手动维护带来了很…

vscode 默认环境路径

1.下面放在项目根目录上&#xff1a; .vscode/settings.json 2.settings.json内容&#xff1a; {"python.analysis.extraPaths": ["${workspaceFolder}"],"python.defaultInterpreterPath": "/shared_disk/users/lbg/envs/py310_see3d/b…

Android 项目中配置了多个 maven 仓库,但依赖还是下载失败,除了使用代理,还有其他方法吗?

文章目录 前言解决方案gradlemaven 仓库 前言 我们在Android 开发的过程中&#xff0c;经常会遇到三方依赖下载不下来的问题。一般情况下我们会在项目的build.gradle文件中配置多个 maven 仓库来解决。 // Top-level build file where you can add configuration options com…

uni-app 引入vconsole web端正常,安卓端报错 Cannot read property ‘sendBeacon‘ of undefined

reportJSException >>>> exception function:createInstanceContext, exception:white screen cause create instanceContext failed,check js stack ->Uncaught TypeError: Cannot read property sendBeacon of undefined vconsole 只支持 web 端&#xff0c;…

火山RTC 7 获得远端裸数据

一、获得远端裸数据 1、获得h264数据 1&#xff09;、远端编码后视频数据监测器 /*** locale zh* type callback* region 视频管理* brief 远端编码后视频数据监测器<br>* 注意&#xff1a;回调函数是在 SDK 内部线程&#xff08;非 UI 线程&#xff09;同步抛出来的&a…

web 自动化之 Unittest 四大组件

文章目录 一、如何开展自动化测试1、项目需求分析&#xff0c;了解业务需求 web 功能纳入自动化测试2、选择何种方式实现自动化测试 二、Unittest 框架三、TestCase 测试用例四、TestFixture 测试夹具 执行测试用例前的前置操作及后置操作五、TestSuite 测试套件 & TestLoa…

42、在.NET 中能够将⾮静态的⽅法覆写成静态⽅法吗?

在.NET中&#xff0c;不能将非静态方法&#xff08;实例方法&#xff09;直接覆写&#xff08;Override&#xff09;为静态方法&#xff08;Static Method&#xff09;。以下是关键原因和解释&#xff1a; 1. 方法绑定的本质区别 实例方法&#xff1a;属于对象的实例&#xf…

8天Python从入门到精通【itheima】-1~5

目录 1节&#xff1a; 1.Python的优势&#xff1a; 2.Python的独具优势的特点&#xff1a; 2节-初识Python&#xff1a; 1.Python的起源 2.Python广泛的适用面&#xff1a; 3节-什么是编程语言&#xff1a; 1.编程语言的作用&#xff1a; 2.编程语言的好处&#xff1a;…

3D迷宫探险:伪3D渲染与运动控制的数学重构

目录 3D迷宫探险:伪3D渲染与运动控制的数学重构引言第一章 伪3D渲染引擎1.1 射线投射原理1.2 纹理透视校正第二章 迷宫生成算法2.1 图论生成模型2.2 复杂度控制第三章 第一人称控制3.1 运动微分方程3.2 鼠标视角控制第四章 碰撞检测优化4.1 层级检测体系4.2 滑动响应算法第五章…

mac一键安装gpt-sovit教程中,homebrew卡住不动的问题

mac一键安装gpt-sovit教程 仅作为安装过程中解决homebrew卡住问题的记录 资源地址 https://www.yuque.com/baicaigongchang1145haoyuangong/ib3g1e/znoph9dtetg437xb#mlAoP 下载一键包 下载后并解压&#xff0c;找到install for mac.sh&#xff0c;终端执行bash空格拖拽in…

git 远程仓库管理详解

Git 的远程仓库管理是多人协作和代码共享的核心功能。以下是 Git 远程仓库管理的详细说明&#xff0c;包括常用操作、命令和最佳实践。 1. 什么是远程仓库&#xff1f; 远程仓库&#xff08;Remote Repository&#xff09;&#xff1a;存储在网络服务器上的 Git 仓库&#xff0…

【超详细教程】安卓模拟器如何添加本地文件?音乐/照片/视频一键导入!

作为一名安卓开发者或手游爱好者&#xff0c;安卓模拟器是我们日常工作和娱乐的重要工具。但很多新手在使用过程中常常遇到一个共同问题&#xff1a;**如何将电脑本地的音乐、照片、视频等文件导入到安卓模拟器中&#xff1f;**今天&#xff0c;我将为大家带来一份全网最详细的…