@target 和 @retention 共同决定注解的使用位置与存活周期:前者校验语法合法性,后者控制反射可读性;二者独立生效,缺一不可,且需匹配实际处理逻辑。

@Target 指定注解能用在哪儿,写错就直接编译失败
Java 编译器会严格校验 @Target 声明的元素类型是否匹配实际使用位置。比如你定义了一个只允许标注方法的注解,却把它加在类上,javac 会立刻报错:java.lang.annotation.AnnotationTypeMismatchException 或更常见的 Cannot apply @XXX to class。
常见错误是以为 ElementType.TYPE 能覆盖内部类、枚举、接口——它确实可以,但如果你还希望注解能用在字段上,就必须显式加上 ElementType.FIELD,不能靠“继承”或“默认包含”。
-
ElementType.METHOD:仅方法,不包括构造器(构造器要用CONSTRUCTOR) -
ElementType.PARAMETER:仅普通方法/构造器参数,Lambda 表达式参数不算 -
ElementType.TYPE_USE:这是个“补丁型”类型,用于泛型、异常声明、new表达式等上下文,和TYPE并不互斥,常需同时声明 - 多个值用大括号:
@Target({ElementType.METHOD, ElementType.TYPE_USE})
@Retention 控制注解存活时间,影响运行时能否反射读取
如果想在运行时通过 getAnnotations() 拿到自定义注解,@Retention 必须设为 RetentionPolicy.RUNTIME。设成 CLASS(默认值)的话,注解保留在字节码里但 JVM 不加载;设成 SOURCE 则只存在于源码,编译完就丢弃。
容易被忽略的是:Spring 的 @Transactional、Lombok 的 @Data 等都依赖 RUNTIME 级别才能工作。而像 @Override 这种只给编译器看的,就是 SOURCE 级别。
立即学习“Java免费学习笔记(深入)”;
- 反射读取注解前,先确认类加载后该注解是否还在:
MyAnnotation.class.getAnnotation(MyAnnotation.class) != null - Android 开发中注意:ProGuard/R8 可能移除
RUNTIME注解,需保留规则:-keepattributes RuntimeVisibleAnnotations -
RetentionPolicy.CLASS在某些 AOP 场景下够用,且比RUNTIME略轻量,但多数框架要求明确写RUNTIME
组合使用时,@Target 和 @Retention 的约束是独立的
这两个元注解之间没有隐含依赖关系。你可以写一个 @Retention(RetentionPolicy.SOURCE) 但 @Target(ElementType.ANNOTATION_TYPE) 的注解——意思是“这个注解只能用来标注其他注解,且只在编译期存在”。这在定义元注解时很常见(比如 Spring 的 @Documented 就是 SOURCE + ANNOTATION_TYPE)。
但反过来说,如果误把 @Retention(RetentionPolicy.SOURCE) 用在需要运行时处理的注解上,反射永远拿不到它,调试时会卡在“注解明明写了,为什么 null?”
- 检查路径:注解定义 →
@Retention值 → 实际使用处是否在@Target允许范围内 → 运行时是否调用了正确的反射 API(如getMethod().getAnnotation()而非getClass().getAnnotation()) - IDE 通常不会高亮
@Retention错误,只有编译或运行时才暴露问题 - 不要为了“保险”把
@Target设成ElementType.ANY,它会让注解滥用变得难以追踪
自定义注解生效的前提:得让处理逻辑真正触发
光有 @Target 和 @Retention 不等于注解就有用。它们只是“挂载许可”和“存活许可”,真正干活的是你写的处理器——可能是编译期注解处理器(javax.annotation.processing.Processor),也可能是运行时通过反射+条件判断的手动逻辑。
比如你写了 @LogExecutionTime 并设了 @Retention(RUNTIME),但没配 Spring AOP 切面或没写 MethodInterceptor,那它就是个摆设。
- 编译期处理:需在
META-INF/services/javax.annotation.processing.Processor中声明处理器类名,并确保javac -processorpath包含对应 jar - 运行时处理:必须主动调用
getDeclaredAnnotations()或框架(如 Spring、Micrometer)已注册的扫描逻辑 - 注解本身不带逻辑,所有行为都来自外部代码对它的响应——这点初学者最容易误解










