你还在用 if 判断参数是否在枚举范围内吗?我已经用上注解了。

简介: 通过自定义注解来实现参数值的枚举范围校验,适用于场景就是当一个 int, String 值是在一个枚举的范围内,通过 Spring MVC 中的参数校验拓展来实现规范的参数校验,保证用户传入参数的有效性。以减少代码中的 if ... else 逻辑。

自定义注解


依赖导入


在我们的 pom.xml  文件中引入 Spring MVC 的依赖,为了方便我们就使用 Spring Boot 的 Starter 即可。


<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
   <version>2.0.6.RELEASE</version>
</dependency>


注解定义


编写自定义注解,我解释一下下面的几个参数:


1、mssage()  默认错误消息, 和标准的 @NotNull等一样,主要是提示一个 message.


2、value() 这里是一个枚举范围,就是参数字段限定的枚举范围。但是需要注意的是,咱们的枚举定义的过程中只要有 value 字段的属性和 getValue  方法。


/**
 * 枚举值限定校验
 * 注意:枚举一定要包含 value 字段
 *
 * @author zhengsh
 * @date 2021-12-08
 */
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {EnumValueValidator.class})
public @interface EnumValue {
    // 默认错误消息
    String message() default "the integer is not one of the enum values";
    // 约束注解在验证时所属的组别
    Class<?>[] groups() default {};
    // 约束注解的有效负载
    Class<? extends Payload>[] payload() default {};
    Class<? extends Enum> value();
    // 同时指定多个时使用
    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @interface List {
        EnumValue[] value();
    }
}


定义校验器


编写自定义校验器, 在 EnumValueValidator自定义校验器中,咱们需要实现 ConstraintValidator 其实说白了在参数封装的过程中,Spring MVC 框架会去调用 initialize方法对当前这个注解的拓展进行初始化,然后在校验过程中会执行  isValid   如果校验通过我们就返回 true , 不通过我们就返回 false 。如果不通过,就会抛出异常,然后异常的错误信息中会包含我们之前定义的 message.


/**
 * 与约束注解关联的校验器
 *
 * @author zhengsh
 * @date 2021-12-08
 */
public class EnumValueValidator implements ConstraintValidator<EnumValue, Object> {
    private Class<? extends Enum> enumClass;
    private static final String METHOD_NAME = "getValue";
    /**
     * 这个方法做一些初始化校验
     *
     * @param constraintAnnotation
     */
    public void initialize(EnumValue constraintAnnotation) {
        enumClass = constraintAnnotation.value();
        try {
            // 先判断该enum是否实现了getValue方法
            enumClass.getDeclaredMethod(METHOD_NAME);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("the enum class has not getValue method", e);
        }
    }
    /**
     * 这个方法写具体的校验逻辑:校验数字是否属于指定枚举类型的范围
     *
     * @param value
     * @param constraintValidatorContext
     * @return
     */
    public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) {
        // 如果为空返回 true , 判空用 @NotNull 等专用注解
        if (Objects.isNull(value)) {
            return true;
        }
        try {
            Enum[] enumConstants = enumClass.getEnumConstants();
            if (enumConstants == null) {
                // 如果不是枚举类型,返回 enumConstants = null
                return true;
            }
            for (Enum e : enumClass.getEnumConstants()) {
                Method declaredMethod = e.getClass().getDeclaredMethod(METHOD_NAME);
                Object obj = declaredMethod.invoke(e);
                if (Objects.equals(obj, value)) {
                    return true;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
}


使用示例


下面是我们使用的例子我们需要做三步:


1、定义限定范围的注解.


2、在入参接受的对象上增加 @EnumValue 注解 .


3、在控制器的请求参数方法上增加 Validated 标记需要进行参数验证处理。

下面来看看具体的代码和实现。


定义枚举


首先我们先定义一个  Gender 枚举


public enum Gender {
    male(0),
    female(1);
    private int value;
    Gender(int value) {
        this.value = value;
    }
    public int getValue() {
        return this.value;
    }
}


使用和依赖


然后我们在我们的入参上面添加自定义注解即可:


public class Person {
  @EnumValue(value = Gender.class, message = "gender 字段不在系统支持的枚举范围内")
  Gender gender;
}


最后我们需要在的Controler 的方法上面加上 @Validated注解,表示对当前参数进行校验。


@RestController
public class PersonController {
    @RequestMapping("/test")
    public String test(@Validated Person person) {
        return "OK";
    }
}


使用总结


在 Java 编程的中,通过注解是一种非常简化代码的方法,就如我们  @EnumValue(Gender.class)注解的使用,我们减少了对参数的判断,而且可以在 EnumValueValidator中统一的做处理,方便以后的拓展,而且可以规范化的做一些开发的基础工作,让我们的业务开发者更加的聚焦业务开发。

validation-api中也是提供了非常多的参数校验枚举比如:@NotNull@Min @Email 等注解在 jakarta.validation.constraints 包下大家都可以去使用的。


参考资料



相关文章
|
25天前
通过反射获取方法返回的类型
通过反射获取方法返回的类型
13 1
|
2月前
|
编译器 C++
C++ 新特性---->函数返回类型后置
C++ 新特性---->函数返回类型后置
|
3月前
|
存储 C#
C# 方法详解:定义、调用、参数、默认值、返回值、命名参数、方法重载全解析
方法是一段代码,只有在调用时才会运行。 您可以将数据(称为参数)传递给方法。 方法用于执行某些操作,也被称为函数。 为什么使用方法?为了重用代码:定义一次代码,然后多次使用。
43 0
|
4月前
自定义封装一个方法让这个方法可以判断所有的数据类型并返回
自定义封装一个方法让这个方法可以判断所有的数据类型并返回
15 0
|
5月前
|
SQL
根据一个类型 获取该类型的 特殊判断 优雅写法
根据一个类型 获取该类型的 特殊判断 优雅写法
|
前端开发 Java 数据库
SpringBoot返回枚举对象中的所有属性以对象的形式返回(一个@JSONType解决)
SpringBoot返回枚举对象中的所有属性以对象的形式返回(一个@JSONType解决)
599 0
|
9月前
|
Java 编译器
重载的方法能否根据返回类型进行区分?
重载的方法不能根据返回类型进行区分。方法的重载是基于方法名称和参数列表来进行区分的,与返回类型无关。这是因为在Java中,编译器在确定要调用哪个重载方法时,仅根据传递给方法的参数来进行决策。
230 0
|
Java Spring
自定义注解判断参数为空
使用Spring的 @Valid和@Validated不好嘛,干嘛要自己造轮子呢.......
|
11月前
|
Java 编译器 Spring