Java注解本质是继承Annotation的接口,编译后由JVM通过动态代理生成实例;@Retention决定可见性,仅RUNTIME可反射获取;处理分编译期(APT代码生成)和运行期(反射解析),本身不执行逻辑。

想在大厂面试中把 Java 注解原理讲透,关键不是堆砌术语,而是讲清“谁、在什么时候、怎么处理注解”,并能一针指出常见误区。下面几个点,覆盖高频追问和底层逻辑,讲清楚就能稳住注解模块的得分。
注解本质是接口,不是类
用 @interface 声明的注解,编译后就是继承 java.lang.annotation.Annotation 的接口。它没有实现类,JVM 运行时靠动态代理生成临时实例——你调用 method.getAnnotation(MyAnno.class) 拿到的对象,是 JDK 用 Proxy.newProxyInstance 创建的代理对象。
这就解释了为什么注解里不能写方法体、不能有 static 字段、所有元素必须带 default 值:接口规则决定的,不是语法限制。
@Retention 决定你能不能“看见”它
注解能不能被反射读到,只看 @Retention 的值:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
SOURCE:只在源码期存在,编译完就丢(如@Override),反射肯定拿不到 -
CLASS:保留在 .class 文件里,但 JVM 加载类时不加载它,反射也读不到 -
RUNTIME:加载进 JVM 方法区,才能用getAnnotation()反射获取
面试官常故意问:“@Retention(CLASS) 的注解,用反射能拿到吗?”答“不能”就过了第一关;若补一句“因为 ClassReader 不会解析 CLASS 级注解进运行时元数据”,加分。
处理时机分两派:编译期靠 APT,运行期靠反射
注解本身不执行任何逻辑,它的价值完全取决于谁、在哪个阶段处理它:
- 编译期处理:用
javax.annotation.processing.Processor,javac 编译时扫描并生成代码(如 Lombok、MapStruct)。优势是零运行时开销,缺点是泛型信息已擦除,无法获知真实类型 - 运行期处理:Spring 的
@Autowired、JUnit 的@Test都走反射路径。每次调用getAnnotation()有性能成本,高频场景需缓存结果
如果被问“Spring 是怎么通过 @Bean 创建对象的”,要能串起来:先扫描类上 @Configuration → 反射读取方法上的 @Bean 注解 → 解析方法签名 → 执行方法体得到 Bean 实例。
元注解不是装饰器,是编译器/JVM 的指令说明书
@Target、@Retention、@Documented、@Inherited 都是给工具链看的“元数据说明书”:
-
@Target(ElementType.METHOD):javac 编译时检查,标在类上直接报错 -
@Inherited:仅对类注解生效,子类自动继承父类的该注解;方法或字段注解不继承,这点常被误答 -
@Documented:影响 javadoc 工具是否把该注解展示在生成的 API 文档里,跟运行逻辑完全无关
讲到这里,顺手提一句:@SpringBootApplication 是三个注解的组合,其中 @EnableAutoConfiguration 的核心就是靠反射读取 @ConditionalOnClass 这类条件注解来决定是否加载某个自动配置类——这就是注解驱动条件装配的典型落地。

















