Java注解是贯穿编译、运行及框架集成的元数据容器,本身不执行逻辑,需由编译器、APT、反射或JVM等处理器读取并响应;掌握关键在于理解谁在何时用、如何用、为何如此设计。

Java 注解不是“写完就忘”的语法糖,而是贯穿编译、运行、框架集成的关键线索。掌握它,重点不在背注解列表,而在理解“谁在什么时候用它、怎么用它、为什么这样设计”。
注解的本质:元数据容器,不是逻辑本身
注解本身不执行任何操作,它只是给代码“贴标签”。真正干活的是读取这些标签的处理器——可能是 javac 编译器(如 @Override)、APT 工具(如 Lombok)、反射机制(如 Spring 的 @Autowired),或是 JVM 运行时(如 @Test 被 JUnit 解析)。写注解前先问:这个信息要被谁读?什么时候读?读完用来做什么?
- 编译期检查类注解(如 @SuppressWarnings)由 javac 直接处理,不进入字节码
- 运行时保留的注解(用 @Retention(RetentionPolicy.RUNTIME))才能通过反射获取
- 自定义注解必须明确声明 @Target(能用在哪儿)和 @Retention(保留到哪一阶段)
从标准注解入手,摸清使用节奏
别一上来就写 @interface。先吃透 JDK 和主流框架里高频出现的几个:
- @Override:强制编译器检查重写关系,写错方法名或参数会报错
- @Deprecated:标记过时 API,配合 @Deprecation Javadoc 更规范
- @FunctionalInterface:确保接口有且仅有一个抽象方法,是 Lambda 使用的前提
- @SafeVarargs:告诉编译器跳过泛型可变参数的类型安全警告(仅用于 final 或 static 方法)
每个都动手改写一个反例——比如故意写错 @Override 的方法签名,看编译器如何反馈,比死记规则更牢。
立即学习“Java免费学习笔记(深入)”;
自定义注解:三步闭环不能少
自己定义注解不是终点,而是起点。完整闭环包括:定义 → 标记 → 解析。
- 定义时用 @Target({ElementType.METHOD, ElementType.TYPE}) 明确作用范围
- 标记时支持属性(如 String value() default ""),并允许重复(加 @Repeatable)
- 解析必须配套实现——要么写 APT 在编译期生成代码,要么用反射在运行期扫描处理(注意性能开销)
例如写一个 @LogExecutionTime,定义后还得配一个切面或代理逻辑去读它、计时、打印日志,否则它就是一张废纸。
框架注解不是魔法,背后都是反射+约定
Spring 的 @Controller、MyBatis 的 @Select、JUnit5 的 @BeforeEach,表面是声明式写法,底层全是反射扫描 + 条件判断 + 动态调用。
- Spring 启动时扫描所有 @Component 子类注解,把类注册为 Bean
- @Autowired 在依赖注入阶段通过字段/方法名+类型匹配查找 Bean
- MyBatis 的 @Insert 注解内容被封装成 SQLSource,在执行时交给 StatementHandler 处理
读懂框架源码里类似 AnnotationUtils.findAnnotation() 或 method.getAnnotation() 的调用点,你就拆开了大部分“黑盒”。
注解能力边界清晰:它负责“说”,不说“做”。把“说什么”设计清楚,再配上靠谱的“听的人”,才是通关核心。


















