这篇文章主要介绍了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;
- }
- }
校验结果
最后