springboot validator枚举值校验功能实现

这篇文章主要介绍了springboot validator枚举值校验功能实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

一、前言

在spring项目中,校验参数功能使用hibernate validator是一个不错的选择,我们的项目中也是使用它来进行校验的,省去了很多难看的校验逻辑,使代码的可读性也大大增加,本章将带你使用hibernate validator自定义注解功能实现一个 枚举值校验的逻辑。

二、需求

我们先明确下我们的需求,在程序开发过程中,我们经常会有一个对象的属性值只能出现在一组常量中的校验需求,例如:用户性别字段gender只能等于MALE/FEMALE这两个其中一个值,用户账号的状态status只能等于:

NORMAL/DISABLED/DELETED其中一个等等,那么我们怎么能更好的校验这个参数呢?我们想拥有一个java注解,把它标记在所要校验的字段上,当开启hibernate validator校验时,就可以校验其字段值是否正确。

三、实现方案

上面提到的一组常量值,我们第一反应应该是定义一个枚举类,尽量不要放在一个统一的constants类下,这样当系统一旦庞大起来,常量是很难维护和查找的,所以前期代码也应该有一些规范性约束,这里我们约定一组常量值时使用枚举,并把该枚举类放在对应的类对象里(以上述所说的用户功能为例,我们应该把GenerEnum、UserStatusEnum枚举放在User.java下,方便查找)

这里我们定义一个叫EnumValue.java的注解类,其下有两个主要参数一个是enumClass用于指定枚举类,enumMethod指定要校验的方法,下面我们看代码实现。

四、代码实现

package com.zhuma.demo.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;

import org.assertj.core.util.Strings;

/**
 * @desc 校验枚举值有效性
 *
 * @author zhumaer
 * @since 10/17/2017 3:13 PM
 */
@Target({ ElementType.METHOD,ElementType.FIELD,ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValue.Validator.class)
public @interface EnumValue {

  String message() default "{custom.value.invalid}";

  Class<?>[] groups() default {};

  Class<? extends Payload>[] payload() default {};

  Class<? extends Enum<?>> enumClass();

  String enumMethod();

  class Validator implements ConstraintValidator<EnumValue,Object> {

    private Class<? extends Enum<?>> enumClass;
    private String enumMethod;

    @Override
    public void initialize(EnumValue enumValue) {
      enumMethod = enumValue.enumMethod();
      enumClass = enumValue.enumClass();
    }

    @Override
    public boolean isValid(Object value,ConstraintValidatorContext constraintValidatorContext) {
      if (value == null) {
        return Boolean.TRUE;
      }

      if (enumClass == null || enumMethod == null) {
        return Boolean.TRUE;
      }

      Class<?> valueClass = value.getClass();

      try {
        Method method = enumClass.getMethod(enumMethod,valueClass);
        if (!Boolean.TYPE.equals(method.getReturnType()) && !Boolean.class.equals(method.getReturnType())) {
          throw new RuntimeException(Strings.formatIfArgs("%s method return is not boolean type in the %s class",enumMethod,enumClass));
        }

        if(!Modifier.isStatic(method.getModifiers())) {
          throw new RuntimeException(Strings.formatIfArgs("%s method is not static method in the %s class",enumClass));
        }

        Boolean result = (Boolean)method.invoke(null,value);
        return result == null ? false : result;
      } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
        throw new RuntimeException(e);
      } catch (NoSuchMethodException | SecurityException e) {
        throw new RuntimeException(Strings.formatIfArgs("This %s(%s) method does not exist in the %s",valueClass,enumClass),e);
      }
    }

  }
}

备注

1) 自定义注解需要实现ConstraintValidator校验类,这里我们定义一个叫Validator的类来实现它,同时实现它下面的两个方法initialize、isValid,一个是初始化参数的方法,另一个就是校验逻辑的方法,本例子中我们将校验类定义在该注解内,用@Constraint(validatedBy = EnumValue.Validator.class)注解指定校验类,内部逻辑实现比较简单就是使用了静态类反射调用验证方法的方式。

2) 对于被校验的方法我们要求,它必须是返回值类型为Boolean或boolean,并且必须是一个静态的方法,返回返回值为null时我们认为是校验不通过的,按false逻辑走。

五、使用演示

校验的目标对象类

package com.zhuma.demo.model.po;

import java.io.Serializable;
import java.util.Date;

import javax.validation.constraints.Pattern;

import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.Range;

import com.zhuma.demo.annotation.EnumValue;
import com.zhuma.demo.validator.CreateGroup;

/**
 * @desc 用户PO

 * @author zhumaer
 * @since 6/15/2017 2:48 PM
 */
public class User implements Serializable {

  private static final long serialVersionUID = 2594274431751408585L;

  /**
   * 用户ID
   */
  private Long id;

  /**
   * 登录密码
   */
  @NotBlank
  private String pwd;

  /**
   * 昵称
   */
  @NotBlank
  @Length(min=1,max=64)
  private String nickname;

  /**
   * 头像
   */
  private String img;

  /**
   * 电话
   */
  @Pattern(regexp = "^1[3-9]\\d{9}$")
  private String phone;

  /**
   * 账号状态
   */
  @EnumValue(enumClass=UserStatusEnum.class,enumMethod="isValidName")
  private String status;

  /**
   * 最新的登录时间
   */
  private Date latestLoginTime;

  /**
   * 最新的登录IP
   */
  private String latestLoginIp;

  private Date createTime;
  private Date updateTime;

  /**
   * 用户状态枚举
   */
  public enum UserStatusEnum {
    /**正常的*/
    NORMAL,/**禁用的*/
    DISABLED,/**已删除的*/
    DELETED;

    /**
     * 判断参数合法性
     */
    public static boolean isValidName(String name) {
      for (UserStatusEnum userStatusEnum : UserStatusEnum.values()) {
        if (userStatusEnum.name().equals(name)) {
          return true;
        }
      }
      return false;
    }
  }

  //省略getter、setter方法

} 

controller类

package com.zhuma.demo.web.user;

import java.util.Date;

import org.springframework.http.HttpStatus;
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.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.zhuma.demo.model.po.User;

/**
 * @desc 用户管理控制器
 *
 * @author zhumaer
 * @since 6/20/2017 16:37 PM
 */
@RestController
@RequestMapping("/users")
public class UserController {

  @PostMapping
  @ResponseStatus(HttpStatus.CREATED)
  public User addUser(@Validated @RequestBody User user) {
    user.setId(10000L);
    user.setCreateTime(new Date());
    return user;
  }

}  

校验结果

springboot validator枚举值校验功能实现

springboot validator枚举值校验功能实现

最后

好啦,一个简单的校验枚举值的注解功能完成了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


今天小编给大家分享的是Springboot下使用Redis管道(pipeline)进行批量操作的介绍,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起...
本篇文章和大家了解一下springBoot项目常用目录有哪些。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。springBoot项目常用目录springBoot项...
本篇文章和大家了解一下Springboot自带线程池怎么实现。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。一: ThreadPoolTaskExecuto1 ThreadP...
这篇文章主要介绍了SpringBoot读取yml文件有哪几种方式,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。Spring Boot读取yml文件的主要方式...
今天小编给大家分享的是SpringBoot配置Controller实现Web请求处理的方法,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧...
本篇文章和大家了解一下SpringBoot实现PDF添加水印的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。简介PDF(Portable Document Form...
本篇文章和大家了解一下解决Springboot全局异常处理与AOP日志处理中@AfterThrowing失效问题的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有...
本篇文章和大家了解一下IDEA创建SpringBoot父子Module项目的实现方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。目录前言1. 软硬件环...
今天小编给大家分享的是springboot获取项目目录路径的方法,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。一定会有所收...
本篇内容主要讲解“SpringBoot+Spring Security无法实现跨域如何解决”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面...
这篇文章主要介绍“vue怎么发送请求到springboot程序”,在日常操作中,相信很多人在vue怎么发送请求到springboot程序问题上存在疑惑,小编查阅了各式资料,整理...
本篇内容主要讲解“Springboot内置的工具类CollectionUtils如何使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家...
本文小编为大家详细介绍“SpringBoot上传文件大小受限如何解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“SpringBoot上传文件大小受限如何解决”文章能帮...
本文小编为大家详细介绍“springboot拦截器如何创建”,内容详细,步骤清晰,细节处理妥当,希望这篇“springboot拦截器如何创建”文章能帮助大家解决疑惑,下面...
本文小编为大家详细介绍“Hikari连接池使用SpringBoot配置JMX监控的方法是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Hikari连接池使用SpringBoot配...
今天小编给大家分享一下SpringBoot如何使用Sa-Token实现权限认证的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大...
这篇文章主要介绍“SpringBoot如何集成SFTP客户端实现文件上传下载”,在日常操作中,相信很多人在SpringBoot如何集成SFTP客户端实现文件上传下...
本篇内容主要讲解“Springboot插件怎么开发”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Springboot插件怎
这篇文章主要介绍“Springboot怎么解决跨域请求问题”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇...
今天小编给大家分享一下如何在SpringBoot2中整合Filter的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文...