上一篇讲了springboot自定义参数校验规则,本篇我们讲解@Validated和@Valid区别。Springboot中参数的校验我们可以使用@Validated和@Valid两个注解,这两个注解有什么区别?那种情况下使用@Validated注解?那种情况下使用@Valid注解?
带着这几个疑问我们先看看这两个注解的源码:
Validated源码如下:
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {
Class>[] value() default {};
}
@Valid源码如下:
@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
public @interface Valid {
}
Java中@Target注解的作用目标如下:
注解 | 描述 |
ElementType.TYPE | 接口、类、枚举 |
ElementType.FIELD | 字段、枚举的常量 |
ElementType.METHOD | 方法 |
ElementType.PARAMETER | 方法参数 |
ElementType.CONSTRUCTOR | 构造函数 |
ElementType.LOCAL_VARIABLE | 局部变量 |
ElementType.ANNOTATION_TYPE | 注解 |
ElementType.PACKAGE | 包 |
通过查看@Target属性,可知:
Valid可以用在:方法,字段、枚举的常量,构造函数,方法参数
Validated可以用在:接口、类、枚举、注解,方法,方法参数
通过上面的对比我们可以看出valid 可以作用在 字段、枚举的常量 上面,而Validated 不可以,所以嵌套校验需要使用 Valid。Validated 多了一个参数用于分组校验,所以如果需要分组校验需要使用Validated。
综上所诉,两者的区别如下:
@Valid:标准JSR-303规范的标记型注解,用来标记验证属性和方法返回值,进行级联和递归校验
@Validated:Spring的注解,是标准JSR-303的一个变种(补充),提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制
比如我们定义了一个PeopleDTO,该DTO下的地址信息是一个对象,我们对地址信息也需要进行校验,校验方式如下:
我们在AddressDTO上面加上了 @Valid注解就可以实现嵌套校验了
package com.validator.demo.api.model.dto;
import java.io.Serializable;
import javax.validation.Valid;
import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.NotBlank;
public class PeopleDTO implements Serializable {
private static final long serialVersionUID = 7515422823626784776L;
@NotBlank(message = "姓名不能为空")
private String name;
@NotBlank(message = "性别不能为空")
private String sex;
@NotBlank(message = "生日不能为空")
@Pattern(regexp = "[0-9]{4}-[0-9]{2}-[0-9]{2}", message = "生日输入数据异常,请确认!")
private String birthday;
@Valid
private AddressDTO address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public AddressDTO getAddress() {
return address;
}
public void setAddress(AddressDTO address) {
this.address = address;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
}
AddressDTO源码如下:
package com.validator.demo.api.model.dto;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotBlank;
public class AddressDTO {
@NotBlank(message = "地址不能为空!")
@Size(min = 6, message = "地址不能小于六个字符,请确认!")
private String addr;
@NotBlank(message = "邮箱不能为空!")
private String email;
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Controller类的代码如下:
package com.validator.demo.api.controller;
import javax.validation.Valid;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.validator.demo.api.base.DateResult;
import com.validator.demo.api.model.dto.PeopleDTO;
import com.validator.demo.api.model.vo.PeopleVO;
@RestController
@RequestMapping("/test/api")
public class ValidatorController {
@PostMapping("/validatortest")
public DateResult test(@RequestBody @Valid PeopleDTO peopleDTO) {
DateResult dateResult = new DateResult();
//具体的业务逻辑
//省略
return dateResult;
}
}
PostMan运行测试结果如下:
在实际项目中,可能多个方法需要使用同一个DTO类来接收参数,而不同方法的校验规则很可能是不一样的。这个时候,简单地在DTO类的字段上加约束注解无法解决这个问题。比如PeopleDTO中新增的时候姓名不可以为空,修改的时候性别不可以为空,生日新增和修改都不可以为空。分组校验实现方式如下:
我们先定义两个分组类,一个是AddGroup,一个是UpdateGroup,源码如下:
package com.validator.demo.api.validation;
public abstract interface AddGroup {
}
package com.validator.demo.api.validation;
public abstract interface UpdateGroup {
}
PeopleDTO代码如下:
package com.validator.demo.api.model.dto;
import java.io.Serializable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.NotBlank;
import com.validator.demo.api.validation.AddGroup;
import com.validator.demo.api.validation.UpdateGroup;
public class PeopleDTO implements Serializable {
private static final long serialVersionUID = 7515422823626784776L;
@NotBlank(groups = { AddGroup.class }, message = "姓名不能为空")
private String name;
@NotBlank(groups = { UpdateGroup.class }, message = "性别不能为空")
private String sex;
@NotNull(groups = { AddGroup.class, UpdateGroup.class }, message = "生日不能为空")
@Pattern(regexp = "[0-9]{4}-[0-9]{2}-[0-9]{2}", message = "生日输入数据异常,请确认!")
private String birthday;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
}
在Controller中我们定义两个方法,一个是新增,一个是修改。在参数的前面添加@Validated注解,并添加相应的分组,代码如下:
package com.validator.demo.api.controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.validator.demo.api.base.DateResult;
import com.validator.demo.api.model.dto.PeopleDTO;
import com.validator.demo.api.model.vo.PeopleVO;
import com.validator.demo.api.validation.AddGroup;
import com.validator.demo.api.validation.UpdateGroup;
@RestController
@RequestMapping("/test/api")
public class ValidatorController {
@PostMapping("/validatoradd")
public DateResult add(@RequestBody @Validated(AddGroup.class) PeopleDTO peopleDTO) {
DateResult dateResult = new DateResult();
//具体的业务逻辑
//省略
return dateResult;
}
@PostMapping("/validatorupdate")
public DateResult update(@RequestBody @Validated(UpdateGroup.class) PeopleDTO peopleDTO) {
DateResult dateResult = new DateResult();
//具体的业务逻辑
//省略
return dateResult;
}
}
使用postman调用验证一下校验。我们的逻辑为新增的时候姓名不可以为空,修改的时候性别不可以为空,生日新增和修改都不可以为空。我们先调用新增方法,不传递姓名,返回结果如下:
然后调用修改方法,不传递性别,返回结果如下:
测试源码,点击链接下载