@Target 和 @Retention 是注解生效的两个硬性开关:@Target 决定注解可用位置(如 METHOD、PARAMETER),缺失则默认仅限 TYPE;@Retention 控制生命周期(SOURCE/CLASS/RUNTIME),仅 RUNTIME 支持反射获取。

@Target 和 @Retention 不是“用来定义注解的语法糖”,而是决定一个注解“能加在哪儿”和“活到什么时候”的两个硬性开关。没配对它们,自定义注解大概率不生效。
为什么 @Target 缺失会导致注解被编译器忽略
Java 编译器看到没声明 @Target 的注解时,默认它只能用在类、接口、枚举上(即 ElementType.TYPE),其他地方比如方法参数、局部变量、返回值一写就报错:Cannot use @MyAnnotation on this element。
- 常见错误现象:给方法加了自定义注解,但运行时
getAnnotations()拿不到;或者 IDE 直接标红 - 必须显式声明目标位置,比如想用在方法上,就得写
@Target(ElementType.METHOD) - 支持多个位置?用数组:
@Target({ElementType.METHOD, ElementType.PARAMETER}) - 别漏掉
ElementType.TYPE_PARAMETER(泛型类型参数),JDK 8+ 才支持,但 Spring 5+ 里已有实际使用
@Retention 的三种策略直接影响运行时能否反射获取
@Retention 控制注解的生命周期。选错策略,Class.getAnnotations() 或 Method.getAnnotation() 就永远为空——不是代码写错了,是注解根本没进字节码或没进 JVM 内存。
-
RetentionPolicy.SOURCE:只保留在源码期,javac 后就丢弃,IDE 可以用,但反射完全看不到 -
RetentionPolicy.CLASS:写进 class 文件,但加载时不进 JVM 方法区,反射也拿不到(多数 Lombok 注解走这条路) -
RetentionPolicy.RUNTIME:唯一能让getAnnotation()成功的选项,Spring、JUnit 都依赖这个 - 性能影响极小,但别滥用:每个
RUNTIME注解都会增加 class 字节码体积,并在类加载时解析元数据
组合使用时最容易踩的坑:@Target 值不兼容 @Retention 范围
这不是语法错误,但会让注解在特定场景下“半失效”。比如你写了 @Target(ElementType.LOCAL_VARIABLE) + @Retention(RetentionPolicy.RUNTIME),看起来没问题,实际上——局部变量注解在 Java 中无法通过反射读取。
立即学习“Java免费学习笔记(深入)”;
- JVM 规范明确禁止运行时获取局部变量上的注解,哪怕你写了
RUNTIME,反射 API 也直接返回 null - 类似地,
ElementType.TYPE_USE(如List<@NonNull String>)需要配合RUNTIME才能在运行时用AnnotatedType获取,但很多老版本反射工具不支持 - 检查 JDK 版本:JDK 8 支持
TYPE_USE和TYPE_PARAMETER,但 Android(Dalvik/ART)直到较新版本才逐步补全 - IDEA/Eclipse 对
TYPE_USE注解的提示可能滞后,建议用javap -v看 class 文件里是否真生成了对应 attribute
真正麻烦的是那种“看着生效、其实漏了一层”的情况:比如注解加在泛型上,@Retention(RUNTIME) 有了,@Target(TYPE_USE) 也写了,但忘了调用 Method.getAnnotatedReturnType() 而不是 Method.getReturnType() —— 这时候不是注解的问题,是你没走对反射入口。










