策略模式与元数据映射模式融合 JSR 380 验证规范实现枚举范围校验

类文件

java 复制代码
@Target({
        ElementType.METHOD,
        ElementType.FIELD,
        ElementType.ANNOTATION_TYPE,
        ElementType.CONSTRUCTOR,
        ElementType.PARAMETER,
        ElementType.TYPE_USE
})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
        validatedBy = {InEnumValidator.class, InEnumCollectionValidator.class}
)
public @interface InEnum {

    /**
     * @return 实现 ArrayValuable 接口的类
     */
    Class<? extends ArrayValuable<?>> value();

    String message() default "必须在指定范围 {value}";

    Class<?>[] groups() default {};

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

}
java 复制代码
public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<?>> {

    private List<?> values;

    @Override
    public void initialize(InEnum annotation) {
        ArrayValuable<?>[] values = annotation.value().getEnumConstants();
        if (values.length == 0) {
            this.values = Collections.emptyList();
        } else {
            this.values = Arrays.asList(values[0].array());
        }
    }

    @Override
    public boolean isValid(Collection<?> list, ConstraintValidatorContext context) {
        if (list == null) {
            return true;
        }
        // 校验通过
        if (CollUtil.containsAll(values, list)) {
            return true;
        }
        // 校验不通过,自定义提示语句
        context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
        context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
                .replaceAll("\\{value}", CollUtil.join(list, ","))).addConstraintViolation(); // 重新添加错误提示语句
        return false;
    }
}
java 复制代码
public class InEnumValidator implements ConstraintValidator<InEnum, Object> {

    private List<?> values;

    @Override
    public void initialize(InEnum annotation) {
        ArrayValuable<?>[] values = annotation.value().getEnumConstants();
        if (values.length == 0) {
            this.values = Collections.emptyList();
        } else {
            this.values = Arrays.asList(values[0].array());
        }
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        // 为空时,默认不校验,即认为通过
        if (value == null) {
            return true;
        }
        // 校验通过
        if (values.contains(value)) {
            return true;
        }
        // 校验不通过,自定义提示语句
        context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
        context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
                .replaceAll("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句
        return false;
    }
}
java 复制代码
public interface ArrayValuable<T> {

    /**
     * @return 数组
     */
    T[] array();

} 
java 复制代码
@Getter
@AllArgsConstructor
public enum CommonStatusEnum implements ArrayValuable<Integer> {

    ENABLE(0, "开启"),
    DISABLE(1, "关闭");

    public static final Integer[] ARRAYS = Arrays.stream(values()).map(CommonStatusEnum::getStatus).toArray(Integer[]::new);

    /**
     * 状态值
     */
    private final Integer status;
    /**
     * 状态名
     */
    private final String name;

    @Override
    public Integer[] array() {
        return ARRAYS;
    }

    public static boolean isEnable(Integer status) {
        return ObjUtil.equal(ENABLE.status, status);
    }

    public static boolean isDisable(Integer status) {
        return ObjUtil.equal(DISABLE.status, status);
    }

}

1. 策略模式(Strategy Pattern)

核心体现

  • InEnumValidator 和 InEnumCollectionValidator 是两个不同的验证策略:
    • InEnumValidator:验证单个值是否在枚举允许范围内
    • InEnumCollectionValidator:验证集合中所有元素是否都在枚举允许范围内
  • 通过 @Constraint(validatedBy = {...}) 动态选择验证策略,符合策略模式的多态特性。

模式优势

  • 开闭原则:新增验证类型(如数组验证)只需添加新策略类,无需修改已有代码。
  • 解耦验证逻辑:将不同场景的校验规则分离到独立类中。

2. 元数据映射模式(Metadata Mapping Pattern)

核心体现

  • ArrayValuable 接口:通过 array() 方法将枚举值映射为统一元数据格式(数组)。
  • InEnum 注解:通过 value() 属性关联具体枚举类,建立校验规则与元数据的映射关系。

代码示例

java 复制代码
// 元数据定义:CommonStatusEnum 提供状态数组
public enum CommonStatusEnum implements ArrayValuable<Integer> {
    ENABLE(0, "开启"), DISABLE(1, "关闭");
    public static final Integer[] ARRAYS = Arrays.stream(values()).map(...);
    @Override public Integer[] array() { return ARRAYS; }
}

// 元数据使用:验证器通过 ArrayValuable 获取校验范围
public class InEnumValidator implements ConstraintValidator<InEnum, Object> {
    private List<?> values;
    @Override public void initialize(InEnum annotation) {
        ArrayValuable<?>[] values = annotation.value().getEnumConstants();
        this.values = Arrays.asList(values[0].array());
    }
}

模式优势

  • 统一元数据接口:所有实现 ArrayValuable 的枚举自动具备提供校验范围的能力。
  • 动态元数据获取:通过反射机制实现运行时元数据解析。

3. JSR 380 规范扩展

核心体现

  • 自定义注解 @InEnum:通过实现 ConstraintValidator 接口扩展标准校验规则。
  • 校验逻辑复用:InEnumValidator 和 InEnumCollectionValidator 复用相同的元数据映射逻辑。

典型应用场景

java 复制代码
// 在 DTO 中使用自定义校验注解
public class UserStatusDTO {
    @InEnum(CommonStatusEnum.class)
    private Integer status; // 自动校验 status 是否为 0 或 1
}

4. 设计优势总结

特性 实现方式
扩展性 新增枚举只需实现 ArrayValuable,无需修改验证逻辑
类型安全 通过泛型 ArrayValuable 保证元数据类型与校验值类型一致
校验语义明确 @InEnum(CommonStatusEnum.class) 直观表达业务约束
错误提示友好 通过 context.buildConstraintViolationWithTemplate 动态生成错误信息

5. 同类设计对比

传统硬编码校验方式:

java 复制代码
// 非模式化的校验逻辑(缺乏扩展性)
if (!(status == 0 || status == 1)) {
    throw new IllegalArgumentException("状态值无效");
}
  • 对比优势:当前设计通过策略+元数据映射,实现校验逻辑与业务代码解耦,符合现代化框架的声明式校验趋势。
相关推荐
如若1232 小时前
Windows SSH + Headscale/Tailscale 组网及免密登录完整配置教程
运维·windows·ubuntu·ssh·内网穿透
开开心心就好2 小时前
音频降噪剪辑工具,音量裁剪一键处理
运维·服务器·前端·spring·ocr·音视频·1024程序员节
Flynn Liang3 小时前
大模型怎么自己调函数?Spring AI 2.0 Tool Calling 自动循环原理 + 完整时序图
java·人工智能·spring·agent·函数调用·springai·toolcalling
ITHAOGE154 小时前
下载 | Win11 25H2 官方正式版ISO映像!(10月更新、消费者版/专业版、商业版/企业版、26200.9711)
windows·科技·微软·电脑
John_Hui4 小时前
Windows 10 安装版本:mysql-8.1.0-winx64.zip
数据库·windows·mysql
kingx34 小时前
Windows Server安全配置深度复盘:关键 Payload 与解题思路
windows·靶场·web·加固
乌暮4 小时前
请求进门先安检:Spring 拦截器与统一功能实战指南
java·开发语言·后端·学习·spring
技术人生0014 小时前
DirectX修复工具下载安装教程-Windows系统游戏dll修复工具下载安装修复教程
windows·游戏
2601_962364976 小时前
企业 PC 安全升级,Windows Hello 商用生物识别模组介绍
windows·安全
2501_916007478 小时前
Windows OpenSSL 生成 iOS 证书,制作 p12 与描述文件,KXBuild 签名实操
windows·vscode·ios·objective-c·个人开发·swift·敏捷流程