java注解本质是元数据标签,不执行逻辑,需由处理器(如spring容器反射扫描)读取并执行;生命周期由@retention决定,runtime最常用,class用于lombok编译期处理,source仅限编译检查。

Java 注解面试题答得准,关键不是背定义,而是讲清楚“它是什么、怎么活、谁来用、为什么这么设计”。面试官想看的是你对注解在真实框架(比如 Spring)中如何起作用的理解,而不是复述 API 文档。
注解的本质不是“代码”,而是“标签”
它本身不执行逻辑,也不改变字节码行为。你写 @Service,JVM 不会因此多做任何事;真正干活的是 Spring 容器——它靠反射扫描到这个标签,再按规则把类注册成 Bean。所以回答时要强调:注解是元数据,处理器才是执行者。自定义注解也一样:没有配套的处理逻辑(编译期 APT 或运行期反射扫描),注解就只是个摆设。
生命周期由 @Retention 决定,选错就“看不见”
这是高频踩坑点。比如你写了 @Retention(RetentionPolicy.CLASS),却想在运行时用反射读取——读不到,因为 JVM 加载类时已丢弃。常见搭配:
- RUNTIME:Spring、JUnit、MyBatis 等框架注解都用它,必须能在运行时被反射读取
-
CLASS:Lombok 的
@Data属于这一类,javac 编译器在生成字节码时读取并改写 class 文件,但运行时不需要 -
SOURCE:像
@Override和@SuppressWarnings,只供编译器检查,不进 class 文件
运行时解析靠反射,核心是 AnnotatedElement 接口
所有能加注解的地方(类、方法、字段等)都实现了这个接口。常用方法要拎得清:
-
isAnnotationPresent(Class extends Annotation>):先判断有没有,比直接 get 更高效 -
getAnnotation(Class<t>)</t>:拿到注解实例,本质是 JDK 动态代理对象,调用value()等方法才真正取值 -
getDeclaredAnnotation(...):只查当前元素声明的注解,不查继承来的(比如父类上的@Transactional默认不会被子类方法继承,除非注解自己加了@Inherited)
自定义注解必须配元注解,缺一不可
光写 @interface MyLog 不够,至少得有:
-
@Retention(RUNTIME):确保能被反射读到 -
@Target({ElementType.METHOD, ElementType.TYPE}):限定能用在哪儿,避免误用(比如不让用在字段上) - (可选但推荐)
@Documented:让 Javadoc 把注解也生成进去 - (可选)
@Inherited:仅对类注解生效,表示子类自动继承父类的该注解
注解元素写法像接口方法:支持基本类型、String、Class、枚举、其他注解,以及它们的数组;每个元素可设 default 值,调用时可省略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











