# 注解的保留策略
保留策略(Retention Policy)
提到策略。那当然想到的就是36计。计谋都有穷尽的时候何况策略。保留策略在java中一定是枚举变量的存在。
package java.lang.annotation;
1
# RetentionPolicy枚举源码
/**
* Annotation retention policy. The constants of this enumerated type
* describe the various policies for retaining annotations. They are used
* in conjunction with the {@link Retention} meta-annotation type to specify
* how long annotations are to be retained.
*
* 注解保留策略。该枚举类型的常量描述了保留注解的各种策略。
* 它们与 {@link Retention} 元注解类型结合使用,以指定注解应被保留多长时间。
*
* @author Joshua Bloch
* @since 1.5
*/
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
* 注解将被编译器丢弃。
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
* 注解将由编译器记录在类文件中,但在运行时不需要被虚拟机保留。这是默认行为。
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
* 注解将由编译器记录在类文件中,并在运行时由虚拟机保留,因此可以通过反射读取。
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# RetentionPolicy枚举值总结表
| 策略值 | 生命周期 | 编译后是否保留 | 运行时是否可用 | 默认行为 | 应用在哪些地方? | 解决了什么问题? | 作用是什么? | 具体框架/注解示例 |
|---|---|---|---|---|---|---|---|---|
| SOURCE | 源码级别 | ❌ 否 | ❌ 否 | - | 1. 编译器 2. IDE工具 3. 代码生成器 4. 静态分析工具 | 1. 防止编码错误 2. 减少样板代码 3. 统一代码规范 4. 自动化代码生成 | 1. 语法检查:验证代码正确性 2. 警告管理:控制编译器警告 3. 代码生成:自动创建重复代码 4. 静态分析:发现潜在问题 | 1. Java内置:@Override(方法重写检查) 2. Java内置:@SuppressWarnings(抑制警告) 3. Lombok:@Getter/@Setter(自动生成getter/setter) 4. Android:@NonNull(空值检查) 5. FindBugs:@Nullable(空指针检查) |
| CLASS | 类文件级别 | ✅ 是 | ❌ 否 | 默认值 | 1. 字节码操作框架 2. AOP框架 3. 类加载器 4. 编译时处理器 | 1. 字节码增强 2. 性能优化 3. 横切关注点分离 4. 编译时代码修改 | 1. 字节码处理:在编译后修改类文件 2. AOP实现:方法拦截和增强 3. 代理生成:创建动态代理类 4. 性能监控:注入监控代码 | 1. AspectJ:@Aspect(切面定义) 2. ASM框架:字节码操作注解 3. ByteBuddy:动态类生成注解 4. Spring AOP:部分内部注解 5. JaCoCo:代码覆盖率分析注解 |
| RUNTIME | 运行时级别 | ✅ 是 | ✅ 是 | - | 1. 依赖注入框架 2. ORM框架 3. Web框架 4. 序列化框架 5. 配置框架 | 1. 依赖管理 2. 对象关系映射 3. 请求路由 4. 数据转换 5. 运行时配置 | 1. IoC容器:管理对象生命周期和依赖 2. 数据映射:数据库表与对象映射 3. 请求处理:HTTP请求与方法映射 4. 序列化:对象与JSON/XML转换 5. 配置驱动:基于注解的配置管理 | 1. Spring:@Component(组件扫描) 2. Spring:@Autowired(依赖注入) 3. JPA/Hibernate:@Entity(实体映射) 4. Spring MVC:@RequestMapping(请求映射) 5. Jackson:@JsonProperty(JSON属性映射) 6. Spring Boot:@Configuration(配置类) 7. JUnit:@Test(测试方法) 8. Swagger:@ApiOperation(API文档) |
# 详细说明
# 1. SOURCE(源码级)
特点:仅在源代码中存在,编译后完全消失
使用方式:只能通过注解处理器(Annotation Processor)在编译时访问
典型示例:
@Override // 编译时检查方法是否真的重写了父类方法 @SuppressWarnings("unchecked") // 告诉编译器忽略特定警告 @Deprecated // 标记已过时的方法或类1
2
3
# 2. CLASS(类文件级)
特点:保留在
.class字节码文件中,但不加载到 JVM 内存使用方式:通过字节码操作框架(如 ASM、ByteBuddy)在类加载前处理
典型示例:
// 很多框架的内部注解使用此策略 // 例如某些 AOP 框架在编译时织入代码1
2
# 3. RUNTIME(运行时级)
特点:完全保留,可通过反射 API 在运行时获取
使用方式:使用
Class.getAnnotations()、Method.getAnnotation()等方法典型示例:
@RestController // Spring MVC 控制器 @RequestMapping("/api") public class MyController { @GetMapping("/users") @ResponseBody public List<User> getUsers() { ... } }1
2
3
4
5
6
7
# 保留策略选择建议
| 场景 | 推荐策略 | 原因 |
|---|---|---|
| 仅编译时检查 | SOURCE | 不增加运行时开销,保持类文件简洁 |
| 框架需要处理字节码 | CLASS | 框架可在类加载时修改字节码,但运行时不需要 |
| 运行时依赖注解 | RUNTIME | 框架需要通过反射读取注解配置 |
| 不确定未来用途 | RUNTIME | 保持灵活性,可随时通过反射访问 |
# 代码示例对比
// SOURCE 策略 - 编译后消失
@Override // RetentionPolicy.SOURCE
public String toString() { return "example"; }
// CLASS 策略 - 保留在类文件中但不加载到 JVM
@SomeAspectAnnotation // RetentionPolicy.CLASS - AspectJ 等使用
public void someMethod() { }
// RUNTIME 策略 - 可通过反射获取
@Component // RetentionPolicy.RUNTIME - Spring 框架
public class MyService {
@Autowired // RetentionPolicy.RUNTIME
private MyRepository repository;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 内存与性能考虑
- SOURCE:无运行时开销
- CLASS:轻微磁盘空间开销(类文件稍大),无内存开销
- RUNTIME:有内存开销(注解对象常驻内存),反射调用有性能开销
选择适当的保留策略有助于平衡功能需求与系统性能。