javax.validation包里@NotNull等注解的使用方式

 更新时间:2024年01月04日 10:28:10   作者:pan_mlpan  
这篇文章主要介绍了javax.validation包里@NotNull等注解的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

在做项目的时候,对pojo和传入的参数进行校验,如果是代码编写,需要很多if来判断

其实可根据一些校验的注解来实现我们的参数校验,主要介绍一下常用的 javax.validation 这个仓库的使用,这里总结一下

1、导包

在项目的pom.xml 文件夹中导入包

 <!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
    <dependency>
      <groupId>javax.validation</groupId>
      <artifactId>validation-api</artifactId>
      <version>1.1.0.Final</version>
    </dependency>

这个验证根据JSR 380规范,validation-api 依赖包含标准验证注解

2、使用

这个包的注解主要有以下这么多,先简单说明(以版本 号2.0.2为标准)

下面再分别介绍

注解名称验证得类型描述
AssertFalseBoolean,boolean被注解的元素属性值必须为false
AssertTrueBoolean,boolean被注解的元素属性值必须为true
DecimalMaxBigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存储的是数字)子类型验证注解的元素值小于等于指定的value值
DecimalMinBigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存储的是数字)子类型验证注解的元素值大于等于指定的value值
DigitsBigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存储的是数字)子类型验证注解的元素值的整数位数和小数位数上限
MaxBigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存储的是数字)子类型被注解的元素值的类型必须为数字,其值必须小于等于指定的最大值
MinBigDecimal,BigInteger, byte,short, int, long,等任何Number或CharSequence(存储的是数字)子类型被注解的元素值的类型必须为数字,其值必须大于等于指定的最小值
EmailCharSequence子类型(如String)
Futurejava.util.Date,java.util.Calendar;Joda Time类库的日期类型被注解的元素值得范围必须为未来的一个时间
FutureOrPresentjava.util.Date,java.util.Calendar;Joda Time类库的日期类型被注解的元素值得范围必须为未来的一个时间或者现在得时间
Pastjava.util.Date,java.util.Calendar;Joda Time类库的日期类型被注解的元素的值范围必须为过去的一个时间
PastOrPresentjava.util.Date,java.util.Calendar;Joda Time类库的日期类型被注解的元素的值范围必须为过去或者现在的一个时间
Negative数值适用于数值并验证它们是严格负数,不包括0
NegativeOrZero数值适用于数值并验证它们是严格负数,包括0
NotBlankCharSequence子类型验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的首位空格
NotEmptyCharSequence子类型、Collection、Map、数组验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
NotNull任意类型被注解的元素属性值必须非null
Null任意类型被注解的元素属性值必须为null
PatternString,任何CharSequence的子类型被注解的元素值必须符合指定的正则表达式
Positive数值适用于数值并验证它们是严格正数,不包括0
PositiveOrZero数值适用于数值并验证它们是严格正数,包括0
SizeString, Collection, Map和数组属性被注解的元素属性值的大小必须在指定范围内

2.0 注解的介绍

// 表明注解可以使用在哪里
// 这里表示注解可以使用在 方法 属性  注解  构造方法  参数  type 
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
//描述保留注解的各种策略,它们与元注解(@Retention)一起指定注释要保留多长时间
@Retention(RetentionPolicy.RUNTIME)
//表明这个注解是由 javadoc记录的
@Documented
public @interface MyOwnAnnotation {
    
}

2.1 注解详细属性的介绍

查看上面所有注解的源码,关于注解中的字段,主要分为以下两种

2.1.1 不能自定义值的

以下面的这些注解为代表

  • AssertFalse AssertTrue
  • PositiveOrZero Positive Negative NegativeOrZero
  • NotNull Null NotEmpty NotBlank
  • FutureOrPresent Future PastOrPresent Past

@AssertFalse为例,该注解的详细内容(源码)

@Documented
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
// 这个表明可以重复使用,
//例如在一个field使用这个注解
// 一般注解只能使用一次
//@Max(value = 11)
//Long num;
// 如果使用了这个@Repeatable标注,那就可以使用两次
//@Max(value = 11)
//@Max(value = 22)
//Long num;
@Repeatable(AssertFalse.List.class)
// 这个Constraint 指定验证类,如果不自定义指定,实现其默认的验证类 ,如果指定
// @Constraint(validatedBy = { MyValidator.class })
@Constraint(validatedBy = {})
public @interface AssertFalse {
    // 校验失败的信息,可以自定义
    String message() default "{javax.validation.constraints.AssertFalse.message}";
    // 这里主要进行将validator进行分类,不同的类group中会执行不同的validator操作
    Class<?>[] groups() default {};
	// 这个主要是指定bean,一般很少使用
    Class<? extends Payload>[] payload() default {};

    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface List {
        AssertFalse[] value();
    }
}

2.1.2 能自定义值的

其他的注解都是可以自定义一些值,作为校验的参照值

@Max为例

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Max.List.class)
@Documented
@Constraint(validatedBy = {})
public @interface Max {
    // 校验失败的信息,可以自定义
    String message() default "{javax.validation.constraints.Max.message}";

    // 这里主要进行将validator进行分类,不同的类group中会执行不同的validator操作
    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
	// 与上面不同的就是多了个value,这个需要自定义参数传入的值与这个value值对比,也就是校验的参考值
    long value();

    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface List {
        Max[] value();
    }
}

2.1.3 groups属性的使用

这个属性的作用就是举个例子:例如我们在传参的时候,加入我们创建一个用户,那么我们再传入的参数是不会包含用户的id,而如果更新这个用户,我们就需要传入用户id,那么根据多种校验规则的时候,这个属性就起作用了。

如果我们在一个属性使用注解的时候,如果不指定groups的时候,其实默认是在Default 组别中

@NotNull(message = "姓名不能为空")
private String name;

等同于
import javax.validation.groups.Default;
@NotNull(message = "姓名不能为空" , groups = {Default.class})
private String name;  

那么根据这个情况,如果我们想自定义组别的时候,我们就可以分为以下几个步骤:

  • 定义组别
  • 使用组别

(1)定义组别

还是这个例子:如果创建一个用户,不用传入用户的id,如果更新这个用户,我们就需要传入用户id

那么我们就定义两个组别,创建用户组别、更新用户组别(其实就是两个接口,里面不用有方法,但是要注意要继承Default接口,理由下面会说

import javax.validation.groups.Default;

public interface CreateUser extends Default {
}

import javax.validation.groups.Default;

public interface UpdateUser extends Default{
}

(2)使用组别

1)首先在pojo中,我们需要使用组别,若属性在不同的组别有不同的校验方式,那么就特殊指定其需要校验的规则,如果不指定,就还是默认按照Default

这里需要注意:定义的组别最好要继承Default接口,不然当我们在指定规则的校验时候,那么不标注groups的属性就不再校验了,因为默认按照Default

@Data
class User{
    	
    	@NotNull(message = "用户id不能为空", groups = UpdateUser.class)
    	private Integer id;
   
        @NotNull(message = "姓名不能为空" ,groups = {CreateUser.class, UpdateUser.class})
        private String name;
    
        @NotNull(message = "性别不能为空")
        private String sex;
    
        @Max(value = 20 ,message = "最大长度为20")
        private String address;
    
        @Email(message = "不满足邮箱格式")
        private String email;
    
        @AssertTrue(message = "字段为true才能通过")
        private boolean isAuth;
    
        @NotBlank(message = "手机号不能为空")
        @Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误")
        private String mobile;
    
        @Future(message = "时间在当前时间之后才可以通过")
        private Date date;
}

2)在接口中使用特定的组别。

两个接:

  • 创建user的时候,指定验证的组别是CreateUser
  • 更新user的时候,指定验证的组别是UpdateUser

(当自定义的组别继承了Default,这里指定验证组别的,属性会根据指定的groups来进行校验,那么没有指定groups的都会校验,如果不继承Default,那没有自定义groups为这个组别的属性就不生效了)

 /**
     * 走参数校验注解的 groups 组合校验
     *
     * @param user
     * @return
     */
@PostMapping("/users/update")
public ResponseDTO updateUser(@RequestBody @Validated(UpdateUser.class) User user) {
     userService.updateById(userDTO);
     return ResponseDTO.success();
}


 /**
     * 走参数校验注解的 groups 组合校验
     *
     * @param user
     * @return
     */
@PostMapping("/users/save")
public ResponseDTO saveUser(@RequestBody @Validated(CreateUser.class) User user) {
     userService.saveUser(userDTO);
     return ResponseDTO.success();
}

3、使用

3.1 第一种方式,pojo作为传参的形式

  • Pojo 定义验证规则以
  • pojo作为参数传参

(1)User pojo类 定义校验规则

@Data
class User{
   
        @NotNull(message = "姓名不能为空")
        private String name;
    
        @NotNull(message = "性别不能为空")
        private String sex;
    
        @Max(value = 20 ,message = "最大长度为20")
        private String address;
    
        @Email(message = "不满足邮箱格式")
        private String email;
    
        @AssertTrue(message = "字段为true才能通过")
        private boolean isAuth;
    
        @NotBlank(message = "手机号不能为空")
        @Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误")
        private String mobile;
    
        @Future(message = "时间在当前时间之后才可以通过")
        private Date date;
}

(2)让校验规则生效。在Controller类的时候,我们只需要利用 @Validated 注解来实现pojo的校验

@RequestMapping("users")
public ResponseDTO saveUser( @RequestBody @Validated User user){
    
}

(3)捕捉验证异常。如果参数校验通过,那么就直接执行接口方法,但是如果失败了,我们如何进行处理

MethodArgumentNotValidException是springBoot中进行绑定参数校验时的异常,需要在springBoot中处理

其他需要处理 ConstraintViolationException异常进行处理

一般像异常捕捉的,可以自定义一个异常捕捉Handler,实现异常捕捉后的数据返回

import com.dto.ResponseDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;

@RestControllerAdvice
public class GlobalExceptionHandler {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private static int DUPLICATE_KEY_CODE = 1001;
    private static int PARAM_FAIL_CODE = 1002;
    private static int VALIDATION_CODE = 1003;

    /**
     * 处理自定义异常
     */
    @ExceptionHandler(BizException.class)
    public ResponseDTO handleRRException(BizException e) {
        logger.error(e.getMessage(), e);
        return new ResponseDTO(e.getCode(), e.getMessage());
    }

    /**
     * 方法参数校验
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseDTO handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        logger.error(e.getMessage(), e);
        return new ResponseDTO(PARAM_FAIL_CODE, e.getBindingResult().getFieldError().getDefaultMessage());
    }

    /**
     * ValidationException
     */
    @ExceptionHandler(ValidationException.class)
    public ResponseDTO handleValidationException(ValidationException e) {
        logger.error(e.getMessage(), e);
        return new ResponseDTO(VALIDATION_CODE, e.getCause().getMessage());
    }

    /**
     * ConstraintViolationException
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseDTO handleConstraintViolationException(ConstraintViolationException e) {
        logger.error(e.getMessage(), e);
        return new ResponseDTO(PARAM_FAIL_CODE, e.getMessage());
    }
	/**
	* 路径异常
	*/
    @ExceptionHandler(NoHandlerFoundException.class)
    public ResponseDTO handlerNoFoundException(Exception e) {
        logger.error(e.getMessage(), e);
        return new ResponseDTO(404, "不好意思,路径不存在,请检查路径是否正确");
    }
	/**
	* 所有其他异常捕捉
	*/
    @ExceptionHandler(Exception.class)
    public ResponseDTO handleException(Exception e) {
        logger.error(e.getMessage(), e);
        return new ResponseDTO(500, "不好意思,系统繁忙,请稍后再试");
    }
}

3.2 第二种方式,restful风格

  • Controller 上加上@Validated
  • 参数上加注解
@RestController
@RequestMapping("user/")
@Validated
public class UserController{
    @RequestMapping("users)
    public ResponseDTO getUser(@RequestParam("userId") @NotNull(message = "用户id不能为空") Long userId){

    }
}

4、自定义使用

如果我们想自定义一个验证的注解,那么需要怎么做呢?

  • 定义一个注解
  • 编写一个验证类
  • 使用

**(1)**我们首先定义一个像上面的@Null 这样的注解

@Documented
// 这里标注该注解是使用在filed和参数上的
@Target({ElementType.PARAMETER, ElementType.FIELD})
// 运行时生效
@Retention(RetentionPolicy.RUNTIME)
// 指定验证的类是哪个   MyValidator 就是第二步做的事情
@Constraint(validatedBy = MyValidator.class)
public @interface MyValid {
	// 提供自定义异常的信息,可以指定默认值
    String message() default "参数不合法";
    // 分组,详细看 上面的介绍 
    Class<?>[] groups() default {};
	// 针对bean的,使用不多
    Class<? extends Payload>[] payload() default {};
}

(2)编写一个验证类

我们需要编写一个 实现 ConstraintValidator 类实现类,来指定我们的校验规则

如果不指定特定注解的情况下,直接使用

// 这个是Max的指定的验证规则源码
public abstract class AbstractMaxValidator<T> implements ConstraintValidator<Max, T> {
    protected long maxValue;

    public AbstractMaxValidator() {
    }
    public void initialize(Max maxValue) {
        this.maxValue = maxValue.value();
    }
    public boolean isValid(T value, ConstraintValidatorContext constraintValidatorContext) {
        if (value == null) {
            return true;
        } else {
            return this.compare(value) <= 0;
        }
    }
    protected abstract int compare(T var1);
}

// 自定义的验证类
public class MyValidator implements ConstraintValidator {
    @Override
    public void initialize(Annotation constraintAnnotation) {
		// 可以获取注解的值 ,一般写在该方法中
    }

    @Override
    public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
        // 编写自己属性验证的规则,o 则是待验证的值
        return false;
    }
}

如果在指定特定注解的情况下,那么我们就可特定 注解

// 自定义的验证类
public class MyValidator implements ConstraintValidator<MyValid , Object> {
    @Override
    public void initialize(MyValid myValid) {
		// 可以获取注解的值 ,一般写在该方法中
    }

    @Override
    public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
        // 编写自己属性验证的规则,o 则是待验证的值
        return false;
    }
}

(3)使用。就跟正常的那些注解一样使用即可。

@Data
public Class Example{
    @NotBlank(message = "姓名不能为空")
    @MyValid(message = "姓名有误,请核对后提交")
    private String name;
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。 

相关文章

  • 浅谈MyBatis 如何执行一条 SQL语句

    浅谈MyBatis 如何执行一条 SQL语句

    Mybatis 是 Java 开发中比较常用的 ORM 框架。在日常工作中,我们都是直接通过 Spring Boot 自动配置,并直接使用,但是却不知道 Mybatis 是如何执行一条 SQL 语句的,下面就一起讲解一下
    2021-05-05
  • 解决Spring导出可以运行的jar包问题

    解决Spring导出可以运行的jar包问题

    最近需要解决Maven项目导入可执行的jar包的问题,如果项目不包含Spring,那么使用mvn assembly:assembly即可,这篇文章主要介绍了Spring导出可以运行的jar包,需要的朋友可以参考下
    2023-03-03
  • Maven中pom.xml文件报错的原因解决

    Maven中pom.xml文件报错的原因解决

    创建Maven项目的时候,如果你选择的Packaging为war,那么就会报错,本文主要介绍了Maven中pom.xml文件报错的原因解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • Java中的collection集合类型总结

    Java中的collection集合类型总结

    Java的集合类型都是对java.util包中Collection接口的继承,这里我们主要介绍依赖于collection的一些主分支,一起来看一下Java中的collection集合类型总结
    2016-05-05
  • Maven打jar包的三种方式(小结)

    Maven打jar包的三种方式(小结)

    这篇文章主要介绍了Maven打jar包的三种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • 通过第三方接口发送短信验证码/短信通知(推荐)

    通过第三方接口发送短信验证码/短信通知(推荐)

    这篇文章主要介绍了通过第三方接口发送短信验证码/短信通知(推荐)的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-08-08
  • 如何使用JavaMail发送邮件

    如何使用JavaMail发送邮件

    这篇文章主要教大家如何使用JavaMail发送邮件在web应用中,实现用户注册成功之后,将用户的注册信息以Email的形式发送到用户的注册邮箱当中,感兴趣的小伙伴们可以参考一下
    2015-12-12
  • Springboot和Jpa实现学生CRUD操作代码实例

    Springboot和Jpa实现学生CRUD操作代码实例

    这篇文章主要介绍了Springboot和Jpa实现学生CRUD操作代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • JavaWeb文件上传下载功能示例解析

    JavaWeb文件上传下载功能示例解析

    这篇文章主要介绍了JavaWeb中的文件上传和下载功能的实现,文件上传和下载功能是非常常用的功能,需要的朋友可以参考下
    2016-06-06
  • springboot实现邮箱发送(激活码)功能的示例代码

    springboot实现邮箱发送(激活码)功能的示例代码

    这篇文章主要为大家详细介绍了如何利用springboot实现邮箱发送(激活码)功能,文中的示例代码简洁易懂,有需要的小伙伴可以跟随小编一起学习一下
    2023-10-10

最新评论