@retention是java中控制注解生命周期的关键元注解,决定注解在source(仅源码)、class(默认,class文件中但运行时不可见)、runtime(运行时可通过反射获取)三个阶段的保留策略,直接影响注解能否被编译器检查、字节码工具处理或反射读取。

@Retention 是 Java 中用来指定自定义注解保留策略的关键元注解。它直接决定了你的注解在编译、类加载或运行时是否还“存在”,从而影响你能否通过反射读取、是否被编译器检查、甚至是否被打包进 class 文件里。
了解三种 RetentionPolicy 值的含义
Java 提供了三个枚举值,对应注解的三个生命周期阶段:
- RetentionPolicy.SOURCE:注解只保留在源代码中,编译时就被丢弃,不会写入 .class 文件。适合用于编译期检查(如 @Override),或生成文档(如某些自定义文档标记)。
- RetentionPolicy.CLASS:注解会写入 .class 文件,但 JVM 加载类时不会将其加载到内存,反射无法获取。这是默认策略(如果你没写 @Retention,默认就是这个)。适用于某些字节码处理工具(如 ASM、lombok 的部分实现)。
-
RetentionPolicy.RUNTIME:注解会写入 .class,并在运行时由 JVM 加载,可通过反射(如
getAnnotation())读取。这是最常用也最灵活的选择,适用于 AOP、序列化框架(如 Jackson)、测试框架(如 JUnit5 的 @Test)、DI 容器(如 Spring 的 @Autowired)等场景。
怎么选?关键看“谁需要读它”
决定用哪个策略,核心是问自己一个问题:哪个环节需要用到这个注解的信息?
- 如果是给 IDE 或 javac 插件用(比如提示错误、自动补全),选 SOURCE;
- 如果是给构建时的字节码增强工具用(比如修改方法逻辑、加日志),选 CLASS;
- 如果是给运行中的程序逻辑用(比如根据注解动态调用方法、注入对象、校验参数),必须选 RUNTIME。
选高了(比如该用 SOURCE 却用了 RUNTIME)不会报错,但会增大 class 文件体积,且可能暴露不该在运行时可见的元数据;选低了(比如该用 RUNTIME 却用了 CLASS),反射就读不到,功能直接失效。
一个典型例子:自定义权限注解
假设你写了个 @RequiresRole("ADMIN") 注解,想在 Spring MVC 控制器方法上使用,并在请求进来时做权限拦截:
- 拦截逻辑在运行时执行 → 需要反射读取该注解 → 必须声明为
@Retention(RetentionPolicy.RUNTIME); - 如果只在编译期做静态检查(比如发现某 Controller 方法有此注解但没配角色表就报错),那
SOURCE就够了; - 如果用字节码插桩方式在方法入口自动插入校验逻辑(不依赖 Spring 拦截器),那
CLASS可能更合适,还能避免反射开销。
别忘了搭配 @Target 使用
@Retention 控制“活多久”,@Target 控制“能标在哪”。两者常一起出现:
@Target(ElementType.METHOD)<br>@Retention(RetentionPolicy.RUNTIME)<br>public @interface RequiresRole {<br> String value();<br>}
没有 @Target,注解可以随意标注在类、方法、字段甚至其他注解上,容易误用;没有 @Retention,就只能靠默认的 CLASS 策略,很可能不满足运行时需求。










