Java注解是编译期与运行时协同的元数据机制,核心在于生命周期(@Retention)、作用目标(@Target)和处理方式(APT或反射)三者联动,辅以元注解(@Documented、@Inherited、@Repeatable)实现高级功能。

Java 注解不是“写个@就完事”的语法糖,而是编译期、运行时协同工作的元数据机制。掌握它,关键在理解生命周期、作用目标和处理方式三者的联动。
注解的生命周期决定它在哪起作用
注解本身不执行逻辑,它的价值取决于 @Retention 声明的保留策略:
- RetentionPolicy.SOURCE:仅在源码阶段存在(如 @Override),编译后丢弃,对 class 文件和运行时完全不可见
- RetentionPolicy.CLASS:保留在 .class 文件中,但 JVM 加载时不加载进内存(javac 自带注解处理器常用此策略)
- RetentionPolicy.RUNTIME:完整保留到运行时,可通过反射获取(如 @Transactional、自定义配置注解)
写注解前先问自己:我需要它在哪个阶段被读取?选错 retention,反射拿不到,APT 也触发不了。
注解的作用目标由 @Target 精确约束
一个注解能标在哪,由 @Target 明确限定。常见组合示例:
- ElementType.METHOD:用于方法(如 @GetMapping)
- ElementType.TYPE:用于类、接口、枚举(如 @Controller)
- ElementType.FIELD:用于字段(如 @Autowired)
- ElementType.PARAMETER:用于方法参数(如 @RequestBody)
- 可叠加使用:@Target({ElementType.TYPE, ElementType.METHOD})
不加 @Target 的注解默认允许用在所有位置,容易误用;加了但范围过窄(比如只允 METHOD 却想标在类上),编译直接报错。
自定义注解要配得上它的用途
声明注解只是第一步,真正发挥作用靠的是“谁来读它、怎么读”。分两种典型路径:
- 编译期处理(APT):写 AnnotationProcessor,在 javac 编译时扫描注解,生成额外代码(如 ButterKnife、Lombok 的 @Data)
- 运行时处理(反射):用 Class/Method/Field#getAnnotations() 或 getAnnotation(Class) 获取,再根据逻辑做动作(如 Spring 的 @Value 解析)
例如定义一个 @LogExecutionTime 注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {}
再配合 Spring AOP 切面,在运行时拦截带该注解的方法并打印耗时——这才是闭环。
别忽略元注解和重复注解支持
Java 提供了几个“注解的注解”,它们是构建高级注解能力的基础:
- @Documented:让注解出现在 Javadoc 中
- @Inherited:允许子类继承父类的注解(仅对类有效,且要求注解 @Retention 是 RUNTIME)
- @Repeatable:支持同一位置多次使用同一注解(需定义容器注解,如 @Scheduled 可重复)
比如想让 @Role("ADMIN") 和 @Role("USER") 同时标注在一个方法上,就必须用 @Repeatable + 容器注解,否则编译拒绝。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











