• 【springboot】validator枚举值校验


    转自:

      https://blog.csdn.net/aiyaya_/article/details/78588200

    一、前言

    在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", 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;
        }
    
    }  

    校验结果

     


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

  • 相关阅读:
    Python--IO模型
    python queue, pipe, manage
    python多线程,event,互斥锁,死锁,递归锁,信号量
    day34 异常处理、断言、socket之ftp协议
    day34 反射、面向对象内置方法:如__str__、面向对象的软件开发
    PHP基础入门(二)【PHP函数基础】
    PHP基础入门详解(一)【世界上最好用的编程语言】
    H5简单拖放(Drag/Drop)
    手机APP ~ MUI——创建页面方法
    bootstrap栅格系统
  • 原文地址:https://www.cnblogs.com/wjqhuaxia/p/12153053.html
Copyright © 2020-2023  润新知