自己实现一个枚举validation校验器

博客介绍了如何使用自定义的`EnumValidation`注解来增强枚举类型的接口校验,该注解允许指定枚举类并检查输入值是否匹配枚举中的某个有效键。通过此注解,可以避免编写大量重复的校验代码,提高代码的可读性和维护性。

背景

平时我们在进行接口校验的时候,我们会使用@Validation相关注解来帮助我们简化代码。其常用的注解其实已经能满足很大一部分需求,但是在进行枚举字段的接口校验的时候没有相关字段,尤其在我们不是使用枚举的name()方法来传递的时候,枚举的校验就需要很多类似的大段的方法来进行校验。而validation本身是提供了扩展的方法的,因此在次基础上实现了一个对枚举进行校验的注解。

基础类

1.EnumValidation注解

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
import java.lang.annotation.*;

/**
 * 自定义枚举验证注解
 */
@Documented
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {EnumValidation.Validator.class})
public @interface EnumValidation {
    /**
     * 错误提示信息
     *
     * @return
     */
    String message() default "Enumeration validation failed";

    /**
     * 校验分组
     *
     * @return
     */
    Class<?>[] groups() default {};

    /**
     * 要检验参数所属的枚举数组
     *
     * @return
     */
    Class<? extends EnumType>[] enums();

    /**
     * 要校验的参数是否可以为空
     *
     * @return
     */
    boolean canBeBlank() default true;

    Class<? extends Payload>[] payload() default {};

    // 验证器内部类
    @Slf4j
    class Validator implements ConstraintValidator<EnumValidation, String> {

        Class<? extends EnumType>[] enums;
        boolean canBeBlank;

        @Override
        public void initialize(EnumValidation constraintAnnotation) {
            this.enums = constraintAnnotation.enums();
            this.canBeBlank = constraintAnnotation.canBeBlank();
        }

        @Override
        public boolean isValid(String value, ConstraintValidatorContext context) {
            //如果校验的字段为空,判断是否通过
            if (StringUtils.isBlank(value) || enums.length < 1) {
                return canBeBlank;
            }

            //校验枚举字段匹配
            for (Class<? extends EnumType> clazz : enums) {
                if (clazz.isEnum()) {
                    EnumType[] enumConstants = clazz.getEnumConstants();
                    for (EnumType enumType : enumConstants) {
                        if (value.equals(enumType.getValidKey())) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }
    }
}

2.EnumType 枚举公共父类

/**
 * 枚举继承公共类
 */
public interface EnumType {

    /**
     * 获取key
     *
     * @return 获取key
     */
    String getValidKey();
}

使用方法

定义这两个类之后,我们只需要在我们定义的注解上继承EnumType,然后实现其getValidKey方法即可

如下:

/**
 * 是/否 枚举
 */
public enum YesOrNoEnum implements EnumType {
    NO(0, "否"),
    YES(1, "是"),
    ;

    YesOrNoEnum(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public int code;
    public String desc;

    @Override
    public String getValidKey() {
        return code + "";
    }
}

然后在需要校验的字段上加上这个注解即可

import lombok.Data;
import org.hibernate.validator.constraints.Range;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

/**
 * 人类
 *
 */
@Data
public class PersonDTO {
    @NotBlank(message = "姓名不能为空")
    @Size(max = 32, message = "姓名长度不能超过320")
    private String name;

    @NotNull(message = "年齡不能为空")
    @Range(min = 0, max = 150, message = "年龄必须在0-150之间")
    private Integer age;

    @EnumValidation(enums = YesOrNoEnum.class, canBeBlank = false, message = "错误的会员标志")
    private Integer memberFlag;
}

<think>首先,用户的问题是基于之前的引用内容:用户想“在数据校验中如何对枚举类添加额外属性的校验逻辑”。具体来说,是“如何在数据验证过程中对枚举类型添加额外属性校验”。 我需要回顾提供的引用: - 引用[1]:描述了Java中的自定义注解`@InEnum`,用于验证字段是否在指定枚举的取值范围内。它包含属性如`enumClass`, `enumMethod`, `ignoreCase`, `showValues`。 - 引用[2]:展示了如何给枚举添加自定义属性,并通过工具类访问这些属性,如获取枚举值的文字描述。 - 引用[3]:提到了`@PositiveOrZero`注解用于数值验证。 - 引用[4]:讨论了Yii框架中的独立验证器。 - 引用[5]:关于Spring框架的验证机制。 用户的问题是针对Java枚举的额外属性校验。引用[1]和[2]特别相关,因为它们涉及Java枚举和自定义属性。 关键点: - 用户想添加额外属性到枚举类,并在数据验证时校验这些属性。 - 例如,在引用[2]中,枚举有自定义属性,如文字描述,但用户想在校验逻辑中使用这些属性。 我的回答需要: - 使用中文。 - 结构清晰,逐步解决问题。 - 确保回答真实可靠。 - 遵循系统级指令:行内数学表达式用$...$,独立公式用$$...$$并单独成段(但这个问题可能不需要数学表达式)。 - 在引用的段落末尾添加引用标识。 - 生成相关问题。 分析问题: - 在引用[1]中,`@InEnum`验证器检查字段值是否匹配枚举的某个值(通过`enumMethod`指定方法)。 - 用户想添加额外属性校验,比如枚举值除了名称外,还有数字属性或其他属性需要验证。 例如,假设枚举有属性`code`或`priority`,用户想验证输入值是否匹配这个额外属性。 步骤: 1. **定义枚举类**:添加额外属性。 2. **创建自定义验证器**:扩展验证逻辑以检查额外属性。 3. **使用注解**:定义自定义注解来指定要验证的属性。 参考引用[1]: - 它有一个`InEnumValidator`,但只验证值是否在枚举中。 - 需要修改或创建新的验证器来处理额外属性。 参考引用[2]: - 它展示了如何给枚举添加自定义属性,如文字描述。 - 但校验逻辑需要集成到验证框架中。 可能的解决方案: - 扩展`@InEnum`注解,添加一个属性来指定要比较的额外属性名。 - 创建新的自定义验证器,读取枚举的额外属性并比较。 例如: - 在`@InEnum`注解中添加一个属性如`propertyToCheck`,默认为`name`,但可以设置为其他方法名。 - 在验证器中,使用反射调用指定方法获取属性值,然后比较。 在回答中: - 解释步骤。 - 提供代码示例。 - 添加引用标识。 - 生成相关问题。 回答结构: 1. **理解需求**:解释问题。 2. **步骤实现**: a. 定义枚举类。 b. 创建自定义注解。 c. 实现验证器逻辑。 d. 使用示例。 3. **总结**。 4. **生成相关问题**。 代码示例要基于Java,使用Spring或类似框架,因为引用[5]提到Spring验证。 确保LaTeX语法正确:如果有数学表达式,用$...$,但这个问题可能不需要。 现在,构建回答。</think>### 在数据校验中对枚举类添加额外属性的校验逻辑 在数据验证过程中,对枚举类型添加额外属性校验的关键在于扩展自定义注解和验证器逻辑。这允许你不仅验证输入值是否在枚举范围内,还能针对枚举的额外属性(如自定义字段)进行校验。以下基于Java Spring框架(参考引用[1]和[5])分步说明实现方法,确保结构清晰、真实可靠。 #### 1. **定义枚举类并添加额外属性** 首先,为枚举类添加自定义属性(如描述、数值等)。参考引用[2],通过为枚举值绑定额外字段来实现: ```java public enum OrderStatus { PENDING(1, "待处理"), PROCESSING(2, "处理中"), COMPLETED(3, "已完成"); private final int code; // 额外属性:状态码 private final String description; // 额外属性:描述 OrderStatus(int code, String description) { this.code = code; this.description = description; } public int getCode() { return code; } public String getDescription() { return description; } } ``` 此枚举包含两个额外属性:`code`(整型状态码)和`description`(描述文本)。现在,我们需要在验证时校验这些属性。 #### 2. **创建自定义注解指定额外属性校验** 扩展引用[1]中的`@InEnum`注解,添加属性来指定要校验的额外属性名。例如,新增`propertyToCheck`属性: ```java import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; @Documented @Constraint(validatedBy = {EnumPropertyValidator.class}) // 指向自定义验证器 @Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface CheckEnumProperty { String message() default "值必须在枚举范围内或属性不匹配"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; Class<? extends Enum<?>> enumClass(); // 指定枚举类 String propertyToCheck() default "name"; // 新增:指定要校验的属性方法名,默认为name() boolean ignoreCase() default false; // 是否忽略大小写 } ``` - `propertyToCheck`属性允许动态指定要比较的额外属性(如`"code"`或`"description"`)。 - 此注解替代了引用[1]的`@InEnum`,但保留了类似结构以支持扩展[^1]。 #### 3. **实现自定义验证器逻辑** 创建验证器`EnumPropertyValidator`,使用反射调用枚举的指定属性方法,并比较输入值: ```java import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.lang.reflect.Method; import java.util.Arrays; public class EnumPropertyValidator implements ConstraintValidator<CheckEnumProperty, Object> { private Class<? extends Enum<?>> enumClass; private String propertyToCheck; private boolean ignoreCase; @Override public void initialize(CheckEnumProperty constraintAnnotation) { this.enumClass = constraintAnnotation.enumClass(); this.propertyToCheck = constraintAnnotation.propertyToCheck(); this.ignoreCase = constraintAnnotation.ignoreCase(); } @Override public boolean isValid(Object value, ConstraintValidatorContext context) { if (value == null) { return true; // 空值由其他注解处理,如@NotNull } try { // 获取枚举所有值 Enum<?>[] enumValues = enumClass.getEnumConstants(); if (enumValues == null) { return false; } // 通过反射获取指定属性方法 Method propertyMethod = enumClass.getMethod(propertyToCheck); for (Enum<?> enumValue : enumValues) { Object propertyValue = propertyMethod.invoke(enumValue); // 比较值(支持忽略大小写) if (ignoreCase && propertyValue instanceof String && value instanceof String) { if (((String) propertyValue).equalsIgnoreCase((String) value)) { return true; } } else if (propertyValue.equals(value)) { return true; } } return false; // 值不匹配任何枚举属性 } catch (Exception e) { throw new RuntimeException("枚举校验失败: " + e.getMessage(), e); } } } ``` - 此验证器通过反射调用`propertyToCheck`指定的方法(如`getCode()`),并比较输入值与属性值。 - 它支持基本类型和字符串,确保健壮性[^1][^5]。 #### 4. **使用示例** 在数据模型中使用自定义注解,指定要校验的额外属性: ```java public class OrderRequest { @CheckEnumProperty( enumClass = OrderStatus.class, propertyToCheck = "code", // 校验枚举的code属性 message = "状态码无效,必须在1-3范围内" ) private Integer statusCode; // 输入值需匹配枚举的code属性 // 其他字段... } ``` - 当输入`statusCode=2`时,验证器会检查`OrderStatus.PROCESSING.getCode() == 2`,成功则通过。 - 如果输入`statusCode=4`,则校验失败,返回错误消息。 - 此方法可复用,适用于任何枚举和额外属性[^2][^5]。 #### 总结 通过扩展自定义注解和验证器,你能灵活地对枚举的额外属性进行校验。这种方法结合了反射和Spring验证框架的优势,确保校验逻辑与业务层解耦。关键点包括: - 定义枚举时添加额外属性。 - 使用注解动态指定要校验的属性。 - 在验证器中通过反射实现通用比较逻辑。 这提升了数据验证的灵活性和可维护性,参考了Spring的验证设计原则[^5]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值