@retention决定注解生命周期,分为source(仅源码)、class(字节码但不加载)和runtime(运行时可用)三级;选错会导致静默失效,需配合@target等元注解完整声明。

@Retention 决定注解能“活到哪一步”——不是所有注解都能被反射读到,也不是所有注解都该留到运行时。关键不在“加不加”,而在“为什么加”和“加了之后真能用上”。
注解生命周期的三个明确阶段
Java 用 RetentionPolicy 枚举定义三种存续级别,每种对应一个不可跨越的边界:
- SOURCE:只存在于 .java 源文件里,javac 编译完就彻底消失。@Override、@SuppressWarnings 都是这一类——编译器检查完就扔,class 文件里查不到。
- CLASS:写进 .class 字节码,但 JVM 加载类时不加载进内存。默认策略,适合 ASM、Javassist 等字节码工具在类加载前分析,反射 API 拿不到。
- RUNTIME:完整保留在运行时方法区,Class.getAnnotation()、Method.getAnnotation() 才能取到。Spring 的 @Component、Jackson 的 @JsonProperty 都依赖它。
选错 Retention 的典型后果
不是报错,而是“静默失效”——代码看着对,结果根本不起作用:
- 写了 @MyAnnotation 却 getAnnotation() 返回 null → 大概率漏了 @Retention(RetentionPolicy.RUNTIME)。
- 想在编译期做校验(比如参数非空提醒),却设成 RUNTIME → 编译器根本不会看它,得配注解处理器(APT)+ SOURCE 才行。
- 大量注解全设 RUNTIME → class 文件变大、类加载稍慢、元数据暴露更多,没收益反而有负担。
验证注解是否真被保留
别靠猜,用 javap 直接看字节码:
- 运行 javap -v YourClass.class,搜索 RuntimeVisibleAnnotations → 有则说明是 RUNTIME 级。
- 搜 RuntimeInvisibleAnnotations → 存在即为 CLASS 级。
- 什么都没搜到 → 很可能是 SOURCE 级,或根本没生效(比如没加 @Retention)。
搭配 @Target 才算完整声明
@Retention 控制“活多久”,@Target 控制“贴在哪”。单独用 @Retention 没问题,但生产环境几乎总要一起用:
- 只允许标在方法上?写 @Target(ElementType.METHOD)。
- 想让子类自动继承?加 @Inherited。
- 希望生成 JavaDoc 时带上注解说明?补 @Documented。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











