java注解本质是继承annotation的接口,通过@target和@retention控制作用位置与生命周期;运行时靠反射+动态代理读取,真正逻辑由处理器(如beanpostprocessor)实现。

Java 注解的面试题,核心是讲清楚“它是什么”和“它怎么工作”,而不是背定义。关键在于把注解和反射、元数据、框架行为串起来说,让面试官看到你理解背后的机制,不是只会写 @Override。
注解本质是接口,不是类
自定义注解用 @interface 声明,编译后生成的是一个继承 java.lang.annotation.Annotation 的接口(不是 class 文件里的普通类)。比如:
-
@MyAnnotation编译后等价于public interface MyAnnotation extends Annotation {} - 注解里声明的方法(如
String value() default "")其实是接口的抽象方法,JVM 运行时通过动态代理返回具体值 - 注解本身不执行逻辑,只提供标记信息——真正干活的是读取它的处理器(比如 Spring 的 BeanPostProcessor 或编译期的 Annotation Processor)
@Retention 和 @Target 必须说清作用
这两个元注解决定注解“在哪能用”和“能活多久”,是解析前提:
-
@Target 指定生效位置:常用
ElementType.METHOD(方法)、ElementType.FIELD(字段)、ElementType.TYPE(类);可多选,用数组写@Target({METHOD, FIELD}) -
@Retention 控制生命周期:
-
SOURCE:只在源码阶段存在(如@Override),编译完就丢掉 -
CLASS:保留在 .class 文件里,但运行时不可见(适合字节码工具处理) -
RUNTIME:运行时可通过反射读取(Spring 大部分注解都用这个)
-
运行时解析靠反射,流程要能画出来
Spring、MyBatis 等框架读取注解,底层就是标准反射调用:
- 先拿到目标元素的反射对象:比如
Class clazz = Class.forName("com.example.UserService"); - 检查是否存在注解:
clazz.isAnnotationPresent(Controller.class) - 获取注解实例:
Controller controller = clazz.getAnnotation(Controller.class)(返回的是 JDK 动态生成的代理对象) - 读属性值:
controller.value()→ 触发代理逻辑,返回你在注解上写的字符串 - 根据值做后续动作:比如 Spring 把这个类注册为 Bean,或 AOP 根据
@Transactional开启事务
自定义注解必须配解析逻辑,否则只是标签
光定义注解没用,得有地方去读它、用它。常见两种方式:
-
编译期处理:用
javax.annotation.processing.Processor,配合@SupportedAnnotationTypes,在 javac 编译时生成代码(Lombok 就这么干) -
运行时处理:在 Spring 环境中,通常实现
BeanPostProcessor或ImportBeanDefinitionRegistrar,在 Bean 创建过程中扫描并响应注解(比如自动注入、参数校验、日志埋点) - 举例:定义
@LogExecutionTime,解析逻辑就在postProcessBeforeInitialization里用反射拿到方法上的该注解,再用 AOP 织入计时逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











