Springboot参数校验@Validated和@Valid区别

@Validated和@Valid区别

上一篇讲了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规范的标记型注解,用来标记验证属性和方法返回值,进行级联和递归校验
@ValidatedSpring的注解,是标准JSR-303的一个变种(补充),提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制

Valid嵌套校验使用方式

比如我们定义了一个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<PeopleVO> test(@RequestBody @Valid PeopleDTO peopleDTO) {
        DateResult<PeopleVO> dateResult = new DateResult<PeopleVO>();

        //具体的业务逻辑
        //省略
        return dateResult;
    }

}

PostMan运行测试结果如下:

Validated 分组校验使用方式

在实际项目中,可能多个方法需要使用同一个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<PeopleVO> add(@RequestBody @Validated(AddGroup.class) PeopleDTO peopleDTO) {
        DateResult<PeopleVO> dateResult = new DateResult<PeopleVO>();

        //具体的业务逻辑
        //省略
        return dateResult;
    }

    @PostMapping("/validatorupdate")
    public DateResult<PeopleVO> update(@RequestBody @Validated(UpdateGroup.class) PeopleDTO peopleDTO) {
        DateResult<PeopleVO> dateResult = new DateResult<PeopleVO>();

        //具体的业务逻辑
        //省略
        return dateResult;
    }
}

使用postman调用验证一下校验。我们的逻辑为新增的时候姓名不可以为空,修改的时候性别不可以为空,生日新增和修改都不可以为空。我们先调用新增方法,不传递姓名,返回结果如下:

然后调用修改方法,不传递性别,返回结果如下:

​测试源码,点击链接下载

猜你喜欢

转载自blog.csdn.net/xinghui_liu/article/details/121465169