Spring中的@Valid 和 @Validated注解你用对了吗
1.概述
本文我们将重点介绍Spring中 @Valid和@Validated注解的区别 。
验证用户输入是否正确是我们应用程序中的常见功能。Spring提供了@Valid和@Validated两个注解来实现验证功能,下面我们来详细介绍它们。
2. @Valid和@Validate注解
在Spring中,我们使用@Valid 注解进行方法级别验证,同时还能用它来标记成员属性以进行验证。
但是,此注释不支持分组验证。@Validated则支持分组验证。
3.例子
让我们考虑一个使用Spring Boot开发的简单用户注册表单。首先,我们只有名称和密码属性:
public class UserAccount {
    @NotNull
    @Size(min = 4, max = 15)
    private String password;
    @NotBlank
    private String name;
    // standard constructors / setters / getters / toString
}
接下来,让我们看一下控制器。在这里,我们将使用带有@Valid批注的saveBasicInfo方法来验证用户输入:
@RequestMapping(value = "/saveBasicInfo", method = RequestMethod.POST)
public String saveBasicInfo(
  @Valid @ModelAttribute("useraccount") UserAccount useraccount,
  BindingResult result,
  ModelMap model) {
    if (result.hasErrors()) {
        return "error";
    }
    return "success";
}
现在让我们测试一下这个方法:
@Test
public void givenSaveBasicInfo_whenCorrectInput`thenSuccess() throws Exception {
    this.mockMvc.perform(MockMvcRequestBuilders.post("/saveBasicInfo")
      .accept(MediaType.TEXT_HTML)
      .param("name", "test123")
      .param("password", "pass"))
      .andExpect(view().name("success"))
      .andExpect(status().isOk())
      .andDo(print());
}
在确认测试成功运行之后,现在让我们扩展功能。下一步的逻辑步骤是将其转换为多步骤注册表格,就像大多数向导一样。第一步,名称和密码保持不变。在第二步中,我们将获取其他信息,例如age 和 phone。因此,我们将使用以下其他字段更新域对象:
public class UserAccount {
    @NotNull
    @Size(min = 4, max = 15)
    private String password;
    @NotBlank
    private String name;
    @Min(value = 18, message = "Age should not be less than 18")
    private int age;
    @NotBlank
    private String phone;
    // standard constructors / setters / getters / toString   
}
但是,这一次,我们将注意到先前的测试失败。这是因为我们没有传递年龄和电话字段。
为了支持此行为,我们引入支持分组验证的@Validated批注。
分组验证,就是将字段分组,分别验证,比如我们将用户信息分为两组:BasicInfo和AdvanceInfo
可以建立两个空接口:
public interface BasicInfo {
}
public interface AdvanceInfo {
}
第一步将具有BasicInfo接口,第二步 将具有AdvanceInfo  。此外,我们将更新UserAccount类以使用这些标记接口,如下所示:
public class UserAccount {
    @NotNull(groups = BasicInfo.class)
    @Size(min = 4, max = 15, groups = BasicInfo.class)
    private String password;
    @NotBlank(groups = BasicInfo.class)
    private String name;
    @Min(value = 18, message = "Age should not be less than 18", groups = AdvanceInfo.class)
    private int age;
    @NotBlank(groups = AdvanceInfo.class)
    private String phone;
    // standard constructors / setters / getters / toString   
}
另外,我们现在将更新控制器以使用@Validated注释而不是@Valid:
@RequestMapping(value = "/saveBasicInfoStep1", method = RequestMethod.POST)
public String saveBasicInfoStep1(
  @Validated(BasicInfo.class)
  @ModelAttribute("useraccount") UserAccount useraccount,
  BindingResult result, ModelMap model) {
    if (result.hasErrors()) {
        return "error";
    }
    return "success";
}
更新后,再次执行测试,现在可以成功运行。现在,我们还要测试这个新方法:
@Test
public void givenSaveBasicInfoStep1`whenCorrectInput`thenSuccess() throws Exception {
    this.mockMvc.perform(MockMvcRequestBuilders.post("/saveBasicInfoStep1")
      .accept(MediaType.TEXT_HTML)
      .param("name", "test123")
      .param("password", "pass"))
      .andExpect(view().name("success"))
      .andExpect(status().isOk())
      .andDo(print());
}
也成功运行!
接下来,让我们看看@Valid对于触发嵌套属性验证是必不可少的。
4.使用@Valid批注标记嵌套对象
@Valid 可以用于嵌套对象。例如,在我们当前的场景中,让我们创建一个 UserAddress 对象:
public class UserAddress {
    @NotBlank
    private String countryCode;
    // standard constructors / setters / getters / toString
}
为了确保验证此嵌套对象,我们将使用@Valid批注装饰属性:
public class UserAccount {
    //...
    @Valid
    @NotNull(groups = AdvanceInfo.class)
    private UserAddress useraddress;
    // standard constructors / setters / getters / toString
}
5. 总结
@Valid保证了整个对象的验证, 但是它是对整个对象进行验证,当仅需要部分验证的时候就会出现问题。 这时候,可以使用@Validated 进行分组验证。
参考
作者:Jadepeng
出处:jqpeng的技术记事本--http://www.cnblogs.com/xiaoqi
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
Spring中的@Valid 和 @Validated注解你用对了吗的更多相关文章
- Spring 中的事务操作、注解、以及 XML 配置
		
事务 事务全称叫数据库事务,是数据库并发控制时的基本单位,它是一个操作集合,这些操作要么不执行,要么都执行,不可分割.例如我们的转账这个业务,就需要进行数据库事务的处理. 转账中至少会涉及到两条 SQ ...
 - 在spring中常被忽视的注解 @Primary
		
在spring 中使用注解,常使用@Autowired, 默认是根据类型Type来自动注入的. 但有些特殊情况,对同一个接口,可能会有几种不同的实现类,而默认只会采取其中一种的情况下 @Primary ...
 - Spring中Bean管理的常用注解
		
在Spring中,主要用于管理bean的注解分为四大类:1.用于创建对象.2.用于给对象的属性注入值.3.用于改变作用的范围.4.用于定义生命周期.这几个在开发中经常接触到,也可以说每天都会遇见.其中 ...
 - Spring 中基于 AOP 的 @AspectJ注解实例
		
@AspectJ 作为通过 Java 5 注释注释的普通的 Java 类,它指的是声明 aspects 的一种风格.通过在你的基于架构的 XML 配置文件中包含以下元素,@AspectJ 支持是可用的 ...
 - Spring中声明式事务的注解@Transactional的参数的总结(REQUIRED和REQUIRES_NEW的与主方法的回滚问题)
		
一.事务的传播行为1.介绍 当事务方法被另一个事务方法调用时,必须指定事务应该如何传播.例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行.2.属性 事务的传播行为可以由传 ...
 - 在使用spring中的ContextConfiguration、test注解时出现的错误
		
错误: 在使用测试注解时出现ContextConfiguration注解和test注解无法正常导包使用的编译异常,如图: 解决办法: 将pom.xml文件中以下依赖管理 中的<scope> ...
 - spring中xml配置方式和注解annoation方式(包括@autowired和@resource)的区别
		
xml文件中配置itemSqlParameterSourceProvider是可以的: <bean id="billDbWriter" class="com.aa. ...
 - Spring中的@response和@request注解
		
@response 标注对象返回的格式为json文本 @requestBody将json对象转换为对应的java类
 - spring中使用@PostConstruct和@PreConstruct注解
		
1.@PostConstruct说明 被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器调用一次,类似于Serclet的inti()方法.被@PostCo ...
 
随机推荐
- socket和http有什么区别?
			
socket是网络传输层的一种技术,跟http有本质的区别,http是应用层的一个网络协议.使用socket技术理论上来讲, 按照http的规范,完全可以使用socket来达到发送http请求的目的, ...
 - CF1457D XOR-gun
			
这道题真的把我秀到了,我首先猜了一波结论,打了一个可持久化 \(\text{Trie}\) 加二分的两只 \(\log_2\) 的做法,发现不能 \(PP\) ,然后就一直改到比赛结束还没改过. 然后 ...
 - 搭建yum仓库服务器
			
环境:服务端centos6.9 客户端要求 能上网(可以ping通baidu.com) 1.yum的配置文件信息在/etc/yum.repos.d/下,我们配置的是自己的网络yum源,所以这些文件我们 ...
 - 购物车 python作业
			
功能要求:要求用户输入总资产,例如:2000显示商品列表,让用户根据序号选择商品,加入购物车购买,如果商品总额大于总资产,提示账户余额不足,否则,购买成功.附加:可充值.某商品移除购物车goods = ...
 - SpringBoot 拦截器和自定义注解判断请求是否合法
			
应用场景举例: 当不同身份的用户请求一个接口时,用来校验用户某些身份,这样可以对单个字段数据进行精确权限控制,具体看代码注释 自定义注解 /** * 对比请求的用户身份是否符合 * @author l ...
 - Office Word文件批量生成软件
			
一.软件用途 如果Word文件模板固定,只是要素信息不同,则可以使用本软件批量生成Word文件. 软件下载地址(2020-12-6更新):https://files.cnblogs.com/files ...
 - zookeeper集群&伪集群模式部署
			
1.什么是单机部署 一台服务器上面部署一个单机版本的zookeeper服务,用于提供服务. 2.什么是集群部署? 集群部署就是多台服务器上面各部署单独的一个zookeeper服务,然后组建一个集群 3 ...
 - 软件工程与UML的第一次课
			
| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/fzzcxy/2018SE1 | | 这个作业要求在哪里 | https://edu.cnblogs.com ...
 - C#面向对象(初级)
			
一.面向对象:创建一个对象,这个对象最终会帮你实现你的需求,尽管其中的过程非常曲折艰难.这也就是所谓的"你办事我放心". 例如: 面向对象:折纸 爸爸开心地用纸折成了一个纸鹤: 妈 ...
 - Filezilla账号设置多个文件夹
			
问题描述 一个账号,使用多个文件目录.虽然可以这样设置,但是实际上客户端使用时只能使用一个目录(主目录). 所以想找一种方式,访问一个主目录时,也可以访问到其他的目录. 官网示例 https://wi ...