java注解本质是描述代码的元数据,需用@target指定作用位置、@retention指定保留策略才能生效;二者缺一不可,且runtime+method等组合才支持运行时反射获取。

Java 中的 Annotation(注解)本质是写在代码里的元数据——也就是“描述代码的数据”。它不参与程序逻辑执行,但能被编译器、IDE、构建工具或运行时框架读取并响应。要让自定义注解真正起作用,@Retention 和 @Target 这两个元注解缺一不可,它们分别控制注解“活多久”和“能加在哪儿”。
@Target:决定注解能用在哪些位置
没加 @Target 的注解,默认只允许标在类、接口、枚举等类型声明上(即 ElementType.TYPE)。一旦你想把它用在方法、参数甚至泛型上,就必须显式声明目标。
- 常见取值包括:METHOD(方法)、FIELD(字段)、PARAMETER(形参)、TYPE_PARAMETER(泛型类型参数,JDK 8+)、TYPE_USE(如
List) - 支持多个位置时,用数组写法:
@Target({METHOD, PARAMETER}) - 写错或漏掉关键类型,比如想标注方法却只写了
@Target(TYPE),编译器会直接报错:“Cannot use @MyAnnotation on this element”
@Retention:决定注解保留到哪个阶段
这个元注解选错,注解就等于“看不见”。哪怕代码里写得再对,反射也拿不到——不是你代码有问题,是注解根本没进 JVM。
-
SOURCE:只存在于源码中,编译后丢弃。适合给 IDE 或编译器看(如
@Override) - CLASS:写入 class 文件,但类加载时不进入 JVM 方法区。Lombok 多数注解走这条路
-
RUNTIME:唯一能让
method.getAnnotation()或clazz.getAnnotation()成功返回值的选项。Spring、JUnit、自定义 AOP 都依赖它
组合使用时的关键细节
@Target 和 @Retention 要互相兼容,否则会出现“半生效”现象:
- 局部变量(
LOCAL_VARIABLE)即使配了@Retention(RUNTIME),反射也拿不到——JVM 规范明确禁止运行时读取局部变量上的注解 -
TYPE_USE注解必须配RUNTIME才能通过AnnotatedType获取,但老版本反射 API 或 Android 环境可能不支持 - 没写 @Retention 时,默认是
CLASS;没写 @Target 时,默认是TYPE。这两个默认值在多数业务场景下都不够用
一个可运行的最小示例
下面是一个可用于方法且能在运行时读取的自定义注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
String value() default "default";
}
然后在方法上使用:@LogExecutionTime("user-service") public void doSomething() { ... },后续就能用反射获取该注解信息做日志或拦截处理。











