springboot validator枚举值校验功能的实现
创始人
2024-12-24 18:41:58
0次

一、前言

在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[] payload() default {};   Class> enumClass();   String enumMethod();   class Validator implements ConstraintValidator {     private Class> 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", enumMethod, 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", enumMethod, 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;  } }  

相关内容

热门资讯

空调连续工作多久需要停机休息 空调连续工作的时间取决于多个因素,包括空调的型号、功率、使用环境和维护情况等。一般来说,空调可以连续...
空调哪个符号是除湿的 空调除湿功能通常用一个水滴形状的符号表示。不同品牌和型号的空调遥控器可能会有所差异,但一般来说,除湿...
格力空调多长时间加一次氟利昂 1、一般情况下,空调对氟利昂的使用量是非常小的,所以在空调使用了四年左右的时候再考虑加也没问题。2、...
空调用多长时间需要加氟一次 一般情况下是五年。当出现以下这几种情况时,空调才需要加氟。1、空调的室内机和室外机各有两个接口,当接...
格调生活·C标思享荟:卡萨帝在... 9月12日的郑州龙湖金融岛,夜风轻拂,灯光璀璨。“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑...
品牌价值破千亿、高端份额持续领... 9月12日,“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑州站”在郑州龙湖金融岛举办。2026...
空调主板坏了修800还是换新的 空调主板坏了是修还是换新,需要考虑多个因素,以下是一些建议:1. 维修成本:如果维修费用较低,比如 ...
空调开多久需要休息多长时间 问题:空调开多久需要休息多长时间回答:1、在使用空调的过程中,一般空调开7~8个小时左右需要休息2~...
中央空调多长时间清洗一次 1、一般空调开机前清洗一次即可,空调开机中间时段再清洗一次,空调关机时清洗一次,这样比较合理。2、空...
中央空调氟利昂多久加一次 看空调制热和制冷程度,很多人的观点认为氟利昂必须一年放一次,其实这个观点是错误的,理解上面有点偏差,...