Spring Validation

Spring Validation

  • Spring Validation
    • 核心概念
    • 核心组件
    • 常用注解
    • 使用示例
    • 高级特性
    • 工作原理
    • 深入细节
    • 实践中的Spring Validation
    • 结论

Spring Validation

在现代Web应用开发中,确保用户输入数据的正确性和合法性是至关重要的。Spring Validation作为Spring框架中的一个重要组成部分,提供了一套强大而灵活的机制来处理数据验证。本文将深入探讨Spring Validation的各个方面,包括其工作原理、核心组件、常用注解、高级特性以及最佳实践。

spring validation允许通过注解的方式来定义对象校验规则,把校验和业务逻辑分离开,让代码编写更加方便。

Spring Validation其实就是对Hibernate Validator进一步的封装,方便在Spring中使用。在Spring中有多种校验的方式

第一种是通过实现org.springframework.validation.Validator接口,然后在代码中调用这个类

第二种是按照Bean Validation方式来进行校验,即通过注解的方式。

第三种是基于方法实现校验

除此之外,还可以实现自定义校验

核心概念

Spring Validation是Spring框架提供的一个特性,用于在应用程序中实现数据验证。这一机制通常用于校验用户输入数据的合法性,确保数据在被进一步处理之前符合特定的规则或标准。Spring Validation提供了一种便捷的方式来进行声明式的数据验证,通过注解和配置,可以轻松地应用在Spring管理的beans上。

核心组件

  • Validator接口:这是Spring Validation的核心接口,定义了validate(Object, Errors)方法用于执行验证逻辑。
  • Errors接口:用于记录验证过程中发现的所有错误。
  • BindingResult:是Errors接口的一个扩展,通常用于表单验证中,包含了验证结果。

常用注解

Spring还支持JSR-303和JSR-349(Bean Validation规范),使得在实体或DTO类上通过注解声明验证规则变得非常简单。一些常用的注解包括:

  • @NotNull:确保字段值非空
  • @Min@Max:验证数字值的范围
  • @Size:验证字符串、集合、数组的大小
  • @Email:验证字符串是否为合法邮箱
  • @Pattern:验证字符串是否匹配正则表达式
  • @NotEmpty 只作用于字符串类型,字符串不为空,并且长度不为0
  • @NotBlank 只作用于字符串类型,字符串不为空,并且trim()后不为空串
  • @DecimalMax(value) 限制必须为一个不大于指定值的数字
  • @DecimalMin(value) 限制必须为一个不小于指定值的数字
  • @Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式

使用示例

spring-web模块使用了hibernate-validation,并且databind模块也提供了相应的数据绑定功能。

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

或者

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

我们只需要引入spring-boot-starter-web依赖即可,如果查看其子依赖,可以发现如下的依赖:

<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId>
</dependency>
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId>
</dependency>

但是为了更加细度的理解,引入如下的依赖:

<dependencies><dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId><version>7.0.5.Final</version></dependency><dependency><groupId>org.glassfish</groupId><artifactId>jakarta.el</artifactId><version>4.0.1</version></dependency>
</dependencies>

假设有一个简单的用户注册的场景,需要验证用户输入的数据:

import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;public class UserRegistrationDto {@NotEmpty(message = "Name cannot be empty")private String name;@Email(message = "Email should be valid")private String email;@Size(min = 8, message = "Password should have at least 8 characters")private String password;// Getters and Setters
}

在Controller中,你可以这样使用Spring Validation:

import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;@Validated
@RestController
public class UserRegistrationController {@PostMapping("/register")public String registerUser(@Valid @RequestBody UserRegistrationDto userDto, BindingResult result) {if (result.hasErrors()) {// 处理验证失败的情况return "Validation errors";}// 保存用户或其他业务逻辑return "User registered successfully";}
}

在上面的例子中,@Valid注解触发了UserRegistrationDto上的验证逻辑,如果数据不满足条件,BindingResult将会包含相应的错误信息,可以根据这些信息给出响应。

而如果请求参数并不是实体对象,仅仅是 String 类型的参数,那么则需要再该参数前加上校验的注解,在 Controller 层之上添加 @Validated 启动校验逻辑。

Spring Validation是处理复杂数据验证逻辑时的一个强大工具,通过简单的配置和注解,可以极大地减少手动验证数据的工作量。

高级特性

Spring Validation不仅仅局限于基本的数据类型验证,它还提供了一些高级特性来处理更复杂的验证场景。

当然,我们可以通过更详细地探讨Spring Validation的工作原理和使用细节来加深理解。

工作原理

Spring Validation的工作流程大致如下:

  1. 客户端请求到达Controller:客户端发送的请求首先到达Spring MVC的Controller层。
  2. 数据绑定:Spring MVC通过HTTP请求参数来填充目标对象的属性,这一过程称为数据绑定(Data Binding)。
  3. 触发验证:如果Controller方法参数使用了@Valid@Validated注解,Spring会自动触发验证过程。
  4. 执行验证规则:Spring通过注册的Validator实现或者JSR-303/JSR-349 Bean Validation的注解来检查对象的属性是否满足条件。
  5. 处理验证结果:验证过程中发现的错误被记录在BindingResultErrors对象中。开发者可以查询这个对象,了解哪些验证规则没有通过,并据此给出相应的响应。

深入细节

分组验证:在复杂的场景中,你可能希望根据不同的场景执行不同的验证规则。Spring Validation支持通过分组来实现这一需求。你可以定义多个接口作为分组标识,然后在验证注解中指定组名,最后在触发验证时通过@Validated注解指定需要验证的组。

自定义验证器:除了使用内置的注解外,你还可以通过实现Validator接口创建自定义的验证逻辑。自定义验证器让你能够处理那些标准注解无法覆盖的复杂场景。

public class CustomValidator implements Validator {@Overridepublic boolean supports(Class<?> clazz) {return SomeObject.class.isAssignableFrom(clazz);}@Overridepublic void validate(Object target, Errors errors) {SomeObject obj = (SomeObject) target;// 实现你的验证逻辑if (/* 验证逻辑 */) {errors.rejectValue("fieldName", "error.code", "Default message");}}
}

上面定义的类,其实就是实现接口中对应的方法,supports方法用来表示此校验用在哪个类型上,validate是设置校验逻辑的地点,其中ValidationUtils,是Spring封装的校验工具类,帮助快速实现校验。

测试:

    public class TestMethod1 {public static void main(String[] args) {//创建UserRegistrationDto对象UserRegistrationDto user = new UserRegistrationDto ();user.setName("lucy");// 创建UserRegistrationDto 对应的DataBinderDataBinder binder = new DataBinder(user);// 设置校验binder.setValidator(new CustomValidator());// 由于UserRegistrationDto对象中的属性为空,所以校验不通过binder.validate();//输出结果BindingResult results = binder.getBindingResult();System.out.println(results.getAllErrors());}}

细解Bean Validation注解实现方式

使用Bean Validation校验方式,就是如何将Bean Validation需要使用的javax.validation.ValidatorFactoryjavax.validation.Validator注入到容器中。spring 默认有一个实现类 LocalValidatorFactoryBean,它实现了上面Bean Validation中的接口,并且也实现了org.springframework.validation.Validator接口。

1.创建配置类,配置LocalValidatorFactoryBean

	@Configurationpublic class ValidationConfig {@Beanpublic LocalValidatorFactoryBean validator() {return new LocalValidatorFactoryBean();}}

2.创建实体类,使用注解定义校验规则

	public class User {@NotNullprivate String name;@Min(0)@Max(120)private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}}

3.使用两种不同的校验器实现

  • 使用jakarta.validation.Validator校验
    @Service
    public class MyService1 {@Autowiredprivate Validator validator;public  boolean validator(User user){Set<ConstraintViolation<User>> sets =  validator.validate(user);return sets.isEmpty();}}
    
  • 使用org.springframework.validation.Validator校验
    @Service
    public class MyService2 {@Autowiredprivate Validator validator;public boolean validaPersonByValidator(User user) {BindException bindException = new BindException(user, user.getName());validator.validate(user, bindException);return bindException.hasErrors();}
    }
    

4.测试

public class TestMethod2 {@Testpublic void testMyService1() {ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);MyService1 myService = context.getBean(MyService1.class);User user = new User();user.setAge(-1);boolean validator = myService.validator(user);System.out.println(validator);}@Testpublic void testMyService2() {ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);MyService2 myService = context.getBean(MyService2.class);User user = new User();user.setName("lucy");user.setAge(130);user.setAge(-1);boolean validator = myService.validaPersonByValidator(user);System.out.println(validator);}
}

基于方法实现校验

1.创建配置类,配置MethodValidationPostProcessor

@Configuration
@ComponentScan("com.atguigu.spring6.validation.method3")
public class ValidationConfig {@Beanpublic MethodValidationPostProcessor validationPostProcessor() {return new MethodValidationPostProcessor();}
}

2.创建实体类,使用注解设置校验规则

public class User {@NotNullprivate String name;@Min(0)@Max(120)private int age;@Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$",message = "手机号码格式错误")@NotBlank(message = "手机号码不能为空")private String phone;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getPhone() {return phone;}public void setPhone(String phone) {this.phone = phone;}
}

3.定义Service类,通过注解操作对象

@Service
@Validated
public class MyService {public String testParams(@NotNull @Valid User user) {return user.toString();}}

4.测试

public class TestMethod3 {@Testpublic void testMyService1() {ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);MyService myService = context.getBean(MyService.class);User user = new User();user.setAge(-1);myService.testParams(user);}
}

实现自定义注解校验

1.自定义校验注解

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {CannotBlankValidator.class})
public @interface CannotBlank {//默认错误消息String message() default "不能包含空格";//分组Class<?>[] groups() default {};//负载Class<? extends Payload>[] payload() default {};//指定多个时使用@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})@Retention(RetentionPolicy.RUNTIME)@Documented@interface List {CannotBlank[] value();}
}

2.编写真正的校验类

public class CannotBlankValidator implements ConstraintValidator<CannotBlank, String> {@Overridepublic void initialize(CannotBlank constraintAnnotation) {}@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {//null时不进行校验if (value != null && value.contains(" ")) {//获取默认提示信息String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();System.out.println("default message :" + defaultConstraintMessageTemplate);//禁用默认提示信息context.disableDefaultConstraintViolation();//设置提示语context.buildConstraintViolationWithTemplate("can not contains blank").addConstraintViolation();return false;}return true;}
}

全局异常处理:在Spring MVC中,你可以使用@ControllerAdvice注解创建一个全局异常处理器。这个处理器可以捕获MethodArgumentNotValidException异常,该异常在对象验证失败时抛出。这样,你就可以在一个地方统一处理所有的验证失败情况,而不是在每个Controller中单独处理。

    @ControllerAdvicepublic class GlobalExceptionHandler {@ExceptionHandler(MethodArgumentNotValidException.class)public ResponseEntity<Object> handleValidationExceptions(MethodArgumentNotValidException ex) {BindingResult result = ex.getBindingResult();List<String> errorMessages = result.getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.toList());// 返回一个合适的响应体return new ResponseEntity<>(errorMessages, HttpStatus.BAD_REQUEST);}}

通过深入了解Spring Validation的工作原理和使用细节,你可以更灵活和有效地利用这个功能来确保数据的正确性和合法性。这不仅可以提高应用程序的健壯性,还可以通过减少不必要的数据库操作等来提升性能。

实践中的Spring Validation

为了更有效地使用Spring Validation,应该遵循一些最佳实践:

  1. 合理使用验证分组:分组可以帮助你在不同的场景下复用相同的模型,避免了为每个场景创建单独的验证模型。

  2. 全局异常处理:利用@ControllerAdvice来统一处理验证异常,可以减少重复的错误处理代码,使得异常处理逻辑更加集中和一致。

  3. 自定义验证消息:通过在验证注解中提供消息模板,可以使得错误消息更加用户友好和容易理解。

  4. 综合利用内置和自定义验证器:内置的验证注解处理大部分常见的验证需求,对于更特殊的验证逻辑,应当考虑实现自定义的验证器。

  5. 持续关注新版本的特性:Spring不断地更新和改进,包括其验证框架。关注并利用这些新特性可以让你的应用更加健壮和高效。

结论

Spring Validation是一个强大的框架,它提供了灵活的验证机制,可以帮助开发者确保应用中的数据的正确性和合法性。通过理解和合理使用它的各种特性和最佳实践,可以极大地提高应用的质量和用户体验。随着对Spring Validation更深入的理解和实践,你将能够更有效地利用这个框架来构建健壮且可维护的Web应用。

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

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

相关文章

22. UE5 RPG使用MMC根据等级设置血量和蓝量(下)

上一篇&#xff0c;我们实现了玩家角色和敌人的等级的获取&#xff0c;使用MMC的提前工作已经准备完成&#xff0c;那么&#xff0c;这一篇讲一下&#xff0c;如何使用MMC&#xff0c;通过角色等级和体力值设置角色的最大血量。 MMC 全称 Mod Magnitude Calculation&#xff0c…

基于springboot的同城宠物帮养照看平台

技术&#xff1a;springbootmysqlvue 一、背景 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统同城上门喂遛宠物…

Java学习六—面向对象

一、关于面向对象 1.1简介 Java 是一种面向对象编程语言&#xff0c;其核心思想是面向对象编程&#xff08;Object-Oriented Programming&#xff0c;OOP&#xff09;。 面向对象编程是一种程序设计范式&#xff0c;它将数据与操作数据的方法&#xff08;函数&#xff09;捆…

软件工程-第三版王立福-第1章 绪论

本书结合IEEE最新发布的软件工程体系SWEBOK&#xff0c;和IEEE/ACM软件工程学科小组公布的软件工程教育知识体系SEEK&#xff0c;北大本科生指定教材。注重基础知识的系统性&#xff0c;选材的先进性及知识的应用。2009年出版 软件开发本质的认识&#xff0c;两大技术问题&…

JUnit5的条件测试、嵌套测试、重复测试

条件测试 JUnit5支持条件注解&#xff0c;根据布尔值判断是否执行测试。 自定义条件 EnabledIf和DisabledIf注解用来设置自定义条件&#xff0c;示例&#xff1a; Test EnabledIf("customCondition") void enabled() { // ... } Test DisabledIf("cust…

代码随想录算法训练营第五十一天|动态规划|309.最佳买卖股票时机含冷冻期、714.买卖股票的最佳时机含手续费、总结

309.最佳买卖股票时机含冷冻期 文章 给定一个整数数组&#xff0c;其中第 i 个元素代表了第 i 天的股票价格 。 设计一个算法计算出最大利润。在满足以下约束条件下&#xff0c;你可以尽可能地完成更多的交易&#xff08;多次买卖一支股票&#xff09;: 你不能同时参与多笔…

Java并发基石ReentrantLock:深入解读其原理与实现

码到三十五 &#xff1a; 个人主页 心中有诗画&#xff0c;指尖舞代码&#xff0c;目光览世界&#xff0c;步履越千山&#xff0c;人间尽值得 ! 在Java的并发编程库中&#xff0c;ReentrantLock是一种非常重要的同步工具&#xff0c;它提供了一种比内置synchronized关键字更加…

科研学习|研究方法——实验法

1.实验方法的渊源 今天我们说物理学、生物学是实验的科学&#xff0c;应该不会有人再持异议了&#xff0c;然而连物理学这样的学科在历史上也并非一开始就是实验科学。在2000多年以前的亚里士多德时代&#xff0c;众人都认为物理学是非实验性质的&#xff0c;物理学成为实验科学…

netty基础_12.用 Netty 自己实现简单的RPC

用 Netty 自己实现简单的RPC RPC 基本介绍我们的RPC 调用流程图己实现 Dubbo RPC&#xff08;基于 Netty&#xff09;需求说明设计说明代码封装的RPCNettyServerNettyServerHandlerNettyClientHandlerNettyClient 接口服务端(provider)HelloServiceImplServerBootstrap 客户端(…

第四百一十四回

文章目录 1. 概念介绍2. 思路与方法2.1 实现思路2.2 实现方法 3. 示例代码4. 内容总结 我们在上一章回中介绍了"自定义标题栏"相关的内容&#xff0c;本章回中将介绍自定义Action菜单.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在这里提到的…

【呼市经开区建设服务项目水、电能耗监测 数采案例】

实施方案 针对能耗采集中的水、电能源数据采集&#xff0c;因客观因素条件&#xff0c;数据采集方面存在较大难度。大多数国网电表485接口由于封签限制&#xff0c;不能实施采集&#xff0c;不让拆机接线&#xff0c;采集实施存在困难。水量能耗采集&#xff0c;存在类似问题&a…

腾讯云GPU服务器深度计算怎么收费?1小时、一个月和一年报价

腾讯云GPU服务器怎么收费&#xff1f;GPU服务器1小时多少钱&#xff1f;一个月收费价格表和一年费用标准&#xff0c;腾讯云百科txybk.com分享腾讯云GPU服务器GPU计算型GN10Xp、GPU服务器GN7、GPU渲染型 GN7vw等GPU实例费用价格&#xff0c;以及NVIDIA Tesla T4 GPU卡和V100详细…

Jmeter Ultimate Thread Group 和 Stepping Thread Group

线程组&#xff1a;使用复杂场景的性能测试 有时候我们做性能测试时&#xff0c;只依靠自带的线程组&#xff0c;显示满足不了性能测试中比较复杂的场景&#xff0c;下面这两种线程组可以帮助你很好的完成复杂的场景 第一种&#xff1a;Stepping Thread Group 在取样器错误后…

Socket类

2.2 Socket类 Socket 类&#xff1a;该类实现客户端套接字&#xff0c;套接字指的是两台设备之间通讯的端点。 构造方法 public Socket(String host, int port) :创建套接字对象并将其连接到指定主机上的指定端口号。如果指定的host是null &#xff0c;则相当于指定地址为回送…

Appium —— 移动应用自动化测试开源工具!

Appium介绍 Appium是一个用于自动化移动应用程序的开源工具&#xff0c;它支持iOS和Android平台。通过Appium&#xff0c;开发人员可以使用各种编程语言&#xff08;如Java、Python、Ruby等&#xff09;编写测试脚本&#xff0c;以自动化测试移动应用程序的功能和用户界面。Ap…

基于springboot+vue的小区团购管理

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

蓝桥杯day2刷题日记-成绩分析-大写-整除序列-日期识别-特殊数的和

由浅入深 P8717 [蓝桥杯 2020 省 AB2] 成绩分析 #include <iostream> using namespace std; int num; double sum; int maxs,mins; int n;int main() {mins1e9;maxs-1e9;sum0;cin>>n;for(int i0;i<n;i){cin>>num;sumnum;maxsmax(maxs,num);minsmin(mins…

如何在Windows系统使用VS Code制作游戏网页并实现无公网IP远程访问

文章目录 前言1. 编写MENJA小游戏2. 安装cpolar内网穿透3. 配置MENJA小游戏公网访问地址4. 实现公网访问MENJA小游戏5. 固定MENJA小游戏公网地址 前言 本篇教程&#xff0c;我们将通过VS Code实现远程开发MENJA小游戏&#xff0c;并通过cpolar内网穿透发布到公网&#xff0c;分…

《操作系统实践-基于Linux应用与内核编程》第10章--实验 Qt聊天程序

前言: 内容参考《操作系统实践-基于Linux应用与内核编程》一书的示例代码和教材内容&#xff0c;所做的读书笔记。本文记录再这里按照书中示例做一遍代码编程实践加深对操作系统的理解。 引用: 《操作系统实践-基于Linux应用与内核编程》 作者&#xff1a;房胜、李旭健、黄…

微信小程序调试、断点调试

1、wxml 查看对应的页面组件 2、console面板可以用来打印信息 3、sources 用来断点调试 4、network面板用来调试接口 5、storage面板 可以查看每个key对应的value内容&#xff0c;这些数据在用户使用小程序时被持久化保存在本地。