【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;
    }

}  

校驗結果

 

 


最后
好啦,一個簡單的校驗枚舉值的注解功能完成了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM