@Valid注解
@Valid 注解通常用于对象属性字段的规则检测
下面我们以新增一个员工为功能切入点,以常规写法为背景,慢慢烘托出 @Valid 注解用法详解。
那么,首先,我们会有一个员工对象 Employee,如下 :
1 public class Employee { 2 3 /** 姓名 */ 4 public String name; 5 6 /** 年龄 */ 7 public Integer age; 8 9 public String getName() { 10 return name; 11 } 12 13 public void setName(String name) { 14 this.name = name; 15 } 16 17 public Integer getAge() { 18 return age; 19 } 20 21 public void setAge(Integer age) { 22 this.age = age; 23 } 24 25 }
然后 Cotroller 中会有一个对应都新增方法 add(),如下:
1 @Controller 2 public class TestController { 3 4 @RequestMapping("/add") 5 @ResponseBody 6 public String add(Employee employee) { 7 // TODO 保存到数据库 8 return "新增员工成功"; 9 } 10 }
现在要求:员工的名称不能为空,且长度不能超过10个字符,那么我们以前的做法大致如下:
1 @Controller 2 public class ValidTest { 3 @RequestMapping("/add") 4 @ResponseBody 5 public String add(Employee employee) { 6 String name = employee.getName(); 7 if(name == null || name.trim().length() == 0){ 8 return "员工名称不能为空"; 9 } 10 if(name.trim().length()>10){ 11 return "员工名称长度不能超过10"; 12 } 13 return "新增员工成功"; 14 } 15 }
除了名称外,我们规定年龄也是必填项,且范围在1到100岁,那么此时,我们需要增加对应判定代码如下:
1 @Controller 2 public class ValidTest { 3 @RequestMapping("/add") 4 @ResponseBody 5 public String add(Employee employee) { 6 String name = employee.getName(); 7 if(name == null || name.trim().length() == 0){ 8 return "员工名称不能为空"; 9 } 10 if(name.trim().length()>10){ 11 return "员工名称长度不能超过10"; 12 } 13 Integer age = employee.getAge(); 14 if(age == null){ 15 return "员工年龄不能为空"; 16 } 17 if(age<1 || age>100){ 18 return "员工年龄范围不对"; 19 } 20 return "新增员工成功"; 21 } 22 }
那么问题来了,现在员工对象 Employee 就 2 个字段,我们就写了 10 多行的代码验证,要是有20个字段,岂不是要写 100 多行代码?通常来说,当一个方法中的无效业务代码量过多时,往往代码设计有问题,当然这不是我们所想看到都结果。
那么如何解决呢?首先大家应该会想到将对应的验证过程抽成一个验证方法,如下:
这样来看,我们的业务方法就清爽多了。
@Controller public class ValidTest { @RequestMapping("/add") @ResponseBody public String add(Employee employee) { if(valid(employee) != null){ return "Error"; } return "新增员工成功"; } private String valid(Employee employee){ String name = employee.getName(); if(name == null || name.trim().length() == 0){ return "员工名称不能为空"; } if(name.trim().length()>10){ return "员工名称长度不能超过10"; } Integer age = employee.getAge(); if(age == null){ return "员工年龄不能为空"; } if(age<1 || age>100){ return "员工年龄范围不对"; } return null; } }
但这种方式只是抽了一个方法,有一种换汤不换药的感觉,虽然业务方法看起来清爽了很多,但书写代码量并没有下降,反而还多出了一个方法,这也不是我们理想中的样子。
此时,我们引出 Spring 中的 @valid 注解,这些问题就可以迎刃而解了,具体如下:
首先,我们在 Maven 配置中引入 @valid 的依赖:
如果你是 springboot 项目,那么可以不用引入了,已经引入了,他就存在于最核心的 web 开发包里面。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.0.5.RELEASE</version> </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 类的属性上打上如下注解:
1 public class Employee { 2 3 /** 姓名 */ 4 @NotBlank(message = "请输入名称") 5 @Length(message = "名称不能超过个 {max} 字符", max = 10) 6 public String name; 7 8 /** 年龄 */ 9 @NotNull(message = "请输入年龄") 10 @Range(message = "年龄范围为 {min} 到 {max} 之间", min = 1, max = 100) 11 public Integer age; 12 13 public String getName() { 14 return name; 15 } 16 17 public void setName(String name) { 18 this.name = name; 19 } 20 21 public Integer getAge() { 22 return age; 23 } 24 25 public void setAge(Integer age) { 26 this.age = age; 27 } 28 }
然后再 Controller 对应方法上,对这个员工标上 @Valid 注解,表示我们对这个对象属性需要进行验证,
1 @Controller 2 public class ValidTest { 3 @RequestMapping("/add") 4 @ResponseBody 5 public String add(@Valid Employee employee) { 6 return "新增员工成功"; 7 } 8 }
既然验证,那么就肯定会有验证结果,所以我们需要用一个东西来存放验证结果,做法也很简单,在参数直接添加一个BindingResult,具体如下:
@Controller public class ValidTest { @RequestMapping("/add") @ResponseBody public String add(@Valid Employee employee, BindingResult bindingResult) { if(bindingResult.hasErrors()){ return bindingResult.getAllErrors().get(0).getDefaultMessage(); } return "新增员工成功"; } }