@Valid 和 @Validated 注解用法详解
Posted 与f
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了@Valid 和 @Validated 注解用法详解相关的知识,希望对你有一定的参考价值。
目录
案例引入
@Valid 详解
@Validated 详解
@Valid 和 @Validated 比较
案例引入
下面我们以新增一个员工为功能切入点,以常规写法为背景,慢慢烘托出 @Valid 和 @Validated 注解用法详解。
那么,首先,我们会有一个员工对象 Employee,如下 :
/** * 员工对象 * * @author sunnyzyq * @since 2019/12/13 */ public class Employee /** 姓名 */ public String name; /** 年龄 */ public Integer age; public String getName() return name; public void setName(String name) this.name = name; public Integer getAge() return age; public void setAge(Integer age) this.age = age;
然后 Cotroller 中会有一个对应都新增方法 add(),如下:
@Controller public class TestController @RequestMapping("/add") @ResponseBody public String add(Employee employee) // TODO 保存到数据库 return "新增员工成功";
现在要求:员工的名称不能为空,且长度不能超过10个字符,那么我们以前的做法大致如下:
写完,我们启动项目测试下,和我们料想中的一样,毫无问题。
但是,除了名称外,我们规定年龄也是必填项,且范围在1到100岁,那么此时,我们需要增加对应判定代码如下:
那么问题来了,现在员工对象 Employee 就 2 个字段,我们就写了 10 多行的代码验证,要是有20个字段,岂不是要写 100 多行代码?通常来说,当一个方法中的无效业务代码量过多时,往往代码设计有问题,当然这不是我们所想看到都结果。
那么如何解决呢?首先大家应该会想到将对应的验证过程抽成一个验证方法,如下:
这样来看,我们的业务方法就清爽多了。
但这种方式只是抽了一个方法,有一种换汤不换药的感觉,虽然业务方法看起来清爽了很多,但书写代码量并没有下降,反而还多出了一个方法,这也不是我们理想中的样子。
@Valid 详解
此时,我们引出 Spring 中的 @valid 注解,这些问题就可以迎刃而解了,具体如下:
首先,我们在 Maven 配置中引入 @valid 的依赖:
如果你是 springboot 项目,那么可以不用引入了,已经引入了,他就存在于最核心的 web 开发包里面。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
如果你不是 springboot 项目,那么引入下面依赖即可:
<dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.1.0.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.4.1.Final</version> </dependency>
那么针对上面情景,我们可以对我们的代码进行优化了。
首先我们在 Employee 类的属性上打上如下注解:
package com.zyq.beans; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Range; /** * 员工对象 * * @author sunnyzyq * @since 2019/12/13 */ public class Employee /** 姓名 */ @NotBlank(message = "请输入名称") @Length(message = "名称不能超过个 max 字符", max = 10) public String name; /** 年龄 */ @NotNull(message = "请输入年龄") @Range(message = "年龄范围为 min 到 max 之间", min = 1, max = 100) public Integer age; public String getName() return name; public void setName(String name) this.name = name; public Integer getAge() return age; public void setAge(Integer age) this.age = age;
然后再 Controller 对应方法上,对这个员工标上 @Valid 注解,表示我们对这个对象属性需要进行验证,
既然验证,那么就肯定会有验证结果,所以我们需要用一个东西来存放验证结果,做法也很简单,在参数直接添加一个BindingResult,具体如下:
对应获取验证结果的代码如下:
OK ! 万事俱备 !我们进行测试...代码不但简洁了很多,结果和预期的也一模一样!
常用注解:
除了刚刚都注解,最后再附加2个常用注解,我就直接贴图了,基本上这6个注解可以解决99%的字段,其他注解我就不贴图了,如果不满足,自己问百度。
@Validated 详解
上面,我们讲述了 @Valid 注解,现在我们来说说 @Validated 这个注解,在我看来,@Validated 是在 @Valid 基础上,做的一个升级版。
我们可以看到,我们在使用 @Valid 进行验证的时候,我们需要用一个对象去接收校验结果,最后根据校验结果判断,从而提示用户。
如果我们把手动校验的这段代码删除或注释掉,那么即使当我们的字段不满足规则时,方法种的程序也是能够被执行的。
比如,我们将字段值置空时,正常情况是会进行提示的。
当我们把校验逻辑注释掉后,再次执行上面的请求后。
可以看到我们的程序继续往后面去执行完成了。
现在,我们去掉方法参数上的 @Valid 注解和其配对的 BindingResult 对象,
然后再校验的对象前面添加上 @Validated 注解。
这个时候,我们再次请求,可以看到,我们请求报400错误了。
而我们通过程序的异常日志来看,提示说是 age 和 name 字段为了空,致使请求失败。
那么,从这里我们可以得知,当我们的数据存在校验不通过的时候,程序就会抛出
org.springframework.validation.BindException 的异常。
在实际开发的过程中,我们肯定不能讲异常直接展示给用户,而是给能看懂的提示。
于是,我们不妨可以通过捕获异常的方式,将该异常进行捕获。
首先我们创建一个校验异常捕获类 ValidExceptionHandler ,然后打上 @RestControllerAdvice 注解,该注解表示他会去抓所有 @Controller 标记类的异常,并在异常处理后返回以 JSON 或字符串的格式响应前端。
我直接将这段代码贴出来吧。
在异常捕捉到后,我们同上面的 @valid 校验一样,只返回第一个错误提示。
package com.zyq.config; import org.springframework.validation.BindException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class ValidExceptionHandler @ExceptionHandler(BindException.class) public String validExceptionHandler(BindException exception) return exception.getAllErrors().get(0).getDefaultMessage();
那么,我们现在重启程序,然后重新请求,就可以发现界面已经不报400错误了,而是直接提示了我们的错误信息。
@Valid 和 @Validated 比较
最后我们来对 @Valid 和 @Validated 两个注解进行总结下:
(1)@Valid 和 @Validated 两者都可以对数据进行校验,待校验字段上打的规则注解(@NotNull, @NotEmpty等)都可以对 @Valid 和 @Validated 生效;
(2)@Valid 进行校验的时候,需要用 BindingResult 来做一个校验结果接收。当校验不通过的时候,如果手动不 return ,则并不会阻止程序的执行;
(3)@Validated 进行校验的时候,当校验不通过的时候,程序会抛出400异常,阻止方法中的代码执行,这时需要再写一个全局校验异常捕获处理类,然后返回校验提示。
(4)总体来说,@Validated 使用起来要比 @Valid 方便一些,它可以帮我们节省一定的代码,并且使得方法看上去更加的简洁。
转 : https://blog.csdn.net/sunnyzyq/article/details/103527380
https://www.cnblogs.com/Marydon20170307/p/15707793.html
@Valid和@Validated的总结区分(转)
@Valid:
@Valid注解用于校验,所属包为:javax.validation.Valid。
① 首先需要在实体类的相应字段上添加用于充当校验条件的注解,如:@Min,如下代码(age属于Girl类中的属性):
@Min(value = 18,message = "未成年禁止入内")
private Integer age;
② 其次在controller层的方法的要校验的参数上添加@Valid注解,并且需要传入BindingResult对象,用于获取校验失败情况下的反馈信息,如下代码:
@PostMapping("/girls") public Girl addGirl(@Valid Girl girl, BindingResult bindingResult) { if(bindingResult.hasErrors()){ System.out.println(bindingResult.getFieldError().getDefaultMessage()); return null; } return girlResposity.save(girl); }
bindingResult.getFieldError.getDefaultMessage()用于获取相应字段上添加的message中的内容,如:@Min注解中message属性的内容
@Validated:
@Valid是javax.validation里的。
@Validated是@Valid 的一次封装,是Spring提供的校验机制使用。@Valid不提供分组功能
@Validated的特殊用法
1、分组
当一个实体类需要多种验证方式时,例:对于一个实体类的id来说,新增的时候是不需要的,对于更新时是必须的。
可以通过groups对验证进行分组
分组接口类(通过向groups分配不同类的class对象,达到分组目的):
package com.valid.interfaces; public interface First { }
实体类:
package com.valid.pojo; import javax.validation.constraints.Size; import org.hibernate.validator.constraints.NotEmpty; import com.valid.interfaces.First; public class People { //在First分组时,判断不能为空 @NotEmpty(groups={First.class}) private String id; //name字段不为空,且长度在3-8之间 @NotEmpty @Size(min=3,max=8) private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getId() { return id; } public void setId(String id) { this.id = id; } }
注:
(1)不分配groups,默认每次都要进行验证
(2)对一个参数需要多种验证方式时,也可通过分配不同的组达到目的。例:
@NotEmpty(groups={First.class}) @Size(min=3,max=8,groups={Second.class}) private String name;
控制类:
package com.valid.controller; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.valid.interfaces.First; import com.valid.pojo.People; @Controller public class FirstController { @RequestMapping("/addPeople") //不需验证ID public @ResponseBody String addPeople(@Validated People p,BindingResult result) { System.out.println("people‘s ID:" + p.getId()); if(result.hasErrors()) { return "0"; } return "1"; } @RequestMapping("/updatePeople") //需要验证ID public @ResponseBody String updatePeople(@Validated({First.class}) People p,BindingResult result) { System.out.println("people‘s ID:" + p.getId()); if(result.hasErrors()) { return "0"; } return "1"; } }
注:
@Validated没有添加groups属性时,默认验证没有分组的验证属性,如该例子:People的name属性。
@Validated没有添加groups属性时,所有参数的验证类型都有分组(即本例中People的name的@NotEmpty、@Size都添加groups属性),则不验证任何参数
2、组序列
默认情况下,不同组别的约束验证是无序的,然而在某些情况下,约束验证的顺序却很重要。
例:
(1)第二个组中的约束验证依赖于一个稳定状态来运行,而这个稳定状态是由第一个组来进行验证的。
(2)某个组的验证比较耗时,CPU 和内存的使用率相对比较大,最优的选择是将其放在最后进行验证。因此,在进行组验证的时候尚需提供一种有序的验证方式,这就提出了组序列的概念。
一个组可以定义为其他组的序列,使用它进行验证的时候必须符合该序列规定的顺序。在使用组序列验证的时候,如果序列前边的组验证失败,则后面的组将不再给予验证。
分组接口类 (通过@GroupSequence注解对组进行排序):
package com.valid.interfaces; public interface First { }
package com.valid.interfaces; public interface Second { }
package com.valid.interfaces; import javax.validation.GroupSequence; @GroupSequence({First.class,Second.class}) public interface Group { }
实体类:
package com.valid.pojo; import javax.validation.constraints.Size; import org.hibernate.validator.constraints.NotEmpty; import com.valid.interfaces.First; import com.valid.interfaces.Second; public class People { //在First分组时,判断不能为空 @NotEmpty(groups={First.class}) private String id; //name字段不为空,且长度在3-8之间 @NotEmpty(groups={First.class}) @Size(min=3,max=8,groups={Second.class}) private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getId() { return id; } public void setId(String id) { this.id = id; } }
3、验证多个对象
一个功能方法上处理多个模型对象时,需添加多个验证结果对象
package com.valid.controller; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.valid.pojo.People; import com.valid.pojo.Person; @Controller public class FirstController { @RequestMapping("/addPeople") public @ResponseBody String addPeople(@Validated People p,BindingResult result,@Validated Person p2,BindingResult result2) { if(result.hasErrors()) { return "0"; } if(result2.hasErrors()) { return "-1"; } return "1"; } }
————————————————
版权声明:本文为CSDN博主「高高程序猿」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/gaojp008/article/details/80583301
以上是关于@Valid 和 @Validated 注解用法详解的主要内容,如果未能解决你的问题,请参考以下文章
Spring boot @Validated注解以及配合@Valid的使用