题目
实现自定义注解@Range进行属性范围校验
信息
- 类型:问答
- 难度:⭐⭐
考点
自定义注解定义, 反射API应用, 注解处理器设计, 异常处理
快速回答
实现步骤:
- 定义
@Range注解包含min/max参数 - 创建校验工具类使用反射获取字段和注解
- 遍历字段检查数值是否在指定范围内
- 抛出
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
最佳实践
- 性能优化:对频繁调用的对象可缓存Class和Field信息
- 扩展性:支持多种类型(如String长度校验)可增加注解类型判断
- 安全:敏感操作应添加
SecurityManager检查 - 错误信息:提供包含字段名、注解值等详细错误信息
常见错误
| 错误类型 | 示例 | 解决方案 |
|---|---|---|
| 未设置运行时保留 | @Retention(RetentionPolicy.SOURCE) | 必须使用RUNTIME |
| 未处理私有字段 | 抛出IllegalAccessException | 调用field.setAccessible(true) |
| 忽略非数值字段 | 对String字段应用@Range | 增加类型检查或扩展注解 |
扩展知识
- Java Bean Validation:标准JSR 380规范(Hibernate Validator实现)
- 组合注解:通过元注解创建
@ValidAge等组合注解 - APT(注解处理器):编译时处理注解生成代码(如Lombok)
- 性能对比:反射比直接方法调用慢100倍,高频场景考虑字节码增强(如ASM)