注解本质是继承annotation的特殊接口,非类也非语法糖;其行为由@retention(source/class/runtime)决定生命周期,由apt(编译期)或反射(运行期)处理,本身不执行逻辑。

面试时讲注解原理,重点不是背定义,而是说清“它是什么”和“它怎么起作用”。一句话概括:注解本身不干活,它只是贴在代码上的标签;真正干活的是编译器或运行时的处理者——谁在什么时候读它、怎么用它,才决定它的效果。
注解的本质是接口,不是类
你写 @interface MyAnno,编译后实际生成的是一个继承 java.lang.annotation.Annotation 的接口,反编译能看到:
public interface MyAnno extends Annotation { String value() default ""; }
它没有实现类,不能 new,也不能写方法体或静态字段——因为它是接口。运行时你通过反射拿到的那个注解实例,其实是 JVM 用动态代理生成的 $ProxyX 对象,背后由 AnnotationInvocationHandler 驱动,从常量池里的 memberValues Map 中取值。
@Retention 是行为开关,选错就拿不到
注解能不能被反射读到,全看 @Retention 的取值:
- SOURCE:只在源码阶段存在,编译完就丢(如 @Override)
- CLASS:进字节码,但类加载时不进方法区,反射拿不到
- RUNTIME:进字节码且保留在运行时,反射 getAnnotation() 才能取到
很多自定义注解失效,就是因为没显式声明 @Retention(RetentionPolicy.RUNTIME),用了默认的 CLASS 策略。
注解生效靠两套机制:编译期和运行期
它自己不会自动触发逻辑,必须有人处理:
- 编译期处理:通过 APT(Annotation Processing Tool),写一个继承 AbstractProcessor 的处理器,在 javac 编译时扫描并生成新代码(如 ButterKnife、Lombok)
- 运行期处理:用反射遍历类/方法/字段的 getAnnotations(),再根据注解内容执行对应逻辑(如 Spring 的 @Transactional、自定义 AOP 拦截器)
框架封装只是把这套流程标准化了,底层仍是反射 + 判断 + 调用。
元注解是“注解的说明书”
@Target、@Retention 这些不是功能注解,它们只告诉编译器和 JVM:“这个注解允许贴在哪”“要保留到什么时候”。比如:
- @Target(ElementType.METHOD) 表示只能加在方法上,加在类上会直接编译报错
- @Inherited 只对类注解有效,子类可继承父类的该注解;方法和字段注解不继承
- @Documented 决定 Javadoc 是否把该注解显示出来
理解这些,才能写出语义清晰、行为可控的自定义注解。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











