侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

实现自定义注解@Range进行属性范围校验

2025-12-9 / 0 评论 / 4 阅读

题目

实现自定义注解@Range进行属性范围校验

信息

  • 类型:问答
  • 难度:⭐⭐

考点

自定义注解定义, 反射API应用, 注解处理器设计, 异常处理

快速回答

实现步骤:

  1. 定义@Range注解包含min/max参数
  2. 创建校验工具类使用反射获取字段和注解
  3. 遍历字段检查数值是否在指定范围内
  4. 抛出IllegalArgumentException异常处理校验失败

核心代码:

public static void validate(Object obj) {
    // 反射获取所有字段
    // 检查@Range注解
    // 字段值范围校验
}
## 解析

问题背景

在业务开发中,经常需要对Java对象的属性值进行校验(如数值范围、字符串长度等)。要求通过自定义注解@Range配合反射机制,实现通用属性校验工具。

解决方案

1. 定义自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range {
    long min() default Long.MIN_VALUE;
    long max() default Long.MAX_VALUE;
    String message() default "Value out of range";
}

注解说明:

  • @Retention(RetentionPolicy.RUNTIME):确保注解在运行时可通过反射获取
  • @Target(ElementType.FIELD):限制注解只能用于字段
  • 包含min/max边界值和自定义错误消息

2. 实现校验工具类

public class Validator {
    public static void validate(Object obj) throws IllegalAccessException {
        Class<?> clazz = obj.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(Range.class)) {
                field.setAccessible(true);
                Range range = field.getAnnotation(Range.class);

                Object value = field.get(obj);
                if (value instanceof Number) {
                    long numValue = ((Number) value).longValue();
                    if (numValue < range.min() || numValue > range.max()) {
                        throw new IllegalArgumentException(
                            field.getName() + ": " + range.message() + 
                            " [" + range.min() + "-" + range.max() + "]"
                        );
                    }
                }
            }
        }
    }
}

3. 使用示例

public class User {
    @Range(min = 1, max = 120, message = "Invalid age")
    private int age;

    @Range(min = 1000, max = 9999)
    private long userId;

    // 构造方法省略
}

// 测试代码
User user = new User(150, 2000);
try {
    Validator.validate(user);
} catch (IllegalAccessException | IllegalArgumentException e) {
    System.err.println("Validation failed: " + e.getMessage());
}

关键实现细节

  • 反射操作:getDeclaredFields()获取所有字段,setAccessible(true)突破私有字段访问限制
  • 类型检查:通过instanceof Number确保只处理数值类型字段
  • 异常处理:校验失败时抛出带详细信息的IllegalArgumentException

最佳实践

  1. 性能优化:对频繁调用的对象可缓存Class和Field信息
  2. 扩展性:支持多种类型(如String长度校验)可增加注解类型判断
  3. 安全:敏感操作应添加SecurityManager检查
  4. 错误信息:提供包含字段名、注解值等详细错误信息

常见错误

错误类型示例解决方案
未设置运行时保留@Retention(RetentionPolicy.SOURCE)必须使用RUNTIME
未处理私有字段抛出IllegalAccessException调用field.setAccessible(true)
忽略非数值字段对String字段应用@Range增加类型检查或扩展注解

扩展知识

  • Java Bean Validation:标准JSR 380规范(Hibernate Validator实现)
  • 组合注解:通过元注解创建@ValidAge等组合注解
  • APT(注解处理器):编译时处理注解生成代码(如Lombok)
  • 性能对比:反射比直接方法调用慢100倍,高频场景考虑字节码增强(如ASM)