反射获取注解实例须用getDeclaredAnnotation()而非isAnnotationPresent(),注意默认值非null、重复注解需用getDeclaredAnnotationsByType(),且注解值仅为编译期字面量。

获取类/方法/字段上的注解实例
反射中拿到注解,核心是调用 getAnnotation() 或 getDeclaredAnnotation()。前者会沿继承链向上查找(包括接口、父类),后者只查当前声明处——多数时候你该用后者,避免误取到父类的同名注解。
常见错误:用 isAnnotationPresent() 判断存在后,直接强转返回值,结果抛 ClassCastException。它返回的是 boolean,不是注解对象。
- 必须用
getDeclaredAnnotation(YourAnno.class)获取实际注解实例 - 注解类型必须在编译期可见(即不能是
@Retention(RetentionPolicy.SOURCE)) - 如果注解本身带
@Inherited,且目标类继承自带该注解的父类,getAnnotation()才可能返回非空
读取注解的属性值(尤其是默认值和数组)
注解属性本质是接口方法,通过反射调用其 getter 即可。但要注意:没有显式赋值的属性,会返回编译期设定的 default 值,而不是 null(除非 default 就是 null)。
数组属性容易踩坑:不能直接用 annotation.values() 拿 Object[],而要用对应类型的数组方法,比如 String[] names = anno.value();。
立即学习“Java免费学习笔记(深入)”;
- 基本类型和 String 属性:直接调用方法名,如
anno.name() - 数组属性:必须用显式类型接收,如
Class>[] groups = anno.groups();,否则编译失败或运行时报ClassCastException - 枚举/Class 类型属性:返回的就是对应枚举常量或
Class对象,无需额外处理
处理重复注解(@Repeatable)
Java 8 引入 @Repeatable 后,同一位置可声明多个同类型注解,但反射 API 不会自动聚合——getDeclaredAnnotation() 只返回第一个,其余被忽略。
正确做法是改用 getDeclaredAnnotationsByType(),它返回 Annotation[],包含所有匹配的重复注解实例。
- 旧写法
clazz.getAnnotation(MyAnno.class)在重复场景下必然漏数据 - 必须用
clazz.getDeclaredAnnotationsByType(MyAnno.class)(注意是ByType,不是ByClass) - 如果注解没加
@Repeatable,ByType和普通getDeclaredAnnotation行为一致,安全降级
注解属性值是动态表达式?别指望反射能解析
反射只能读取编译后固化在 class 文件里的字面值。像 Spring 的 @Value("${port}") 或 Lombok 的 @Getter(lazy = true),里面的字符串或布尔值是原始字面量,不是运行时计算结果。
比如 @RequestMapping("/api/v${version}"),反射拿到的 value() 就是字符串 "/api/v${version}",不会替换成真实版本号。
- 所有占位符、SpEL 表达式、条件逻辑,都是框架在反射之后自行解析的
- 想模拟框架行为?得自己实现表达式引擎,反射不负责这事
- 测试时若依赖注解值做分支判断,务必确认你读到的是原始字符串,而非期望中的“渲染后结果”










