java注解性能差异由@retention策略决定:runtime需反射调用,适用于spring组件、aop切面等运行时场景;class仅存于字节码,适合lombok、dagger等编译期处理;source仅限源码阶段,用于@override等编译检查。

Java注解的反射性能差异,本质上是由@Retention策略决定的——不是注解本身慢,而是你让它在哪一阶段“露面”,直接决定了它是否参与运行时开销。
必须用 RUNTIME 的场景:反射是刚需,别省
当你的逻辑在应用启动后或请求处理中,明确依赖getAnnotation()、isAnnotationPresent()等反射调用时,RUNTIME 是唯一可行选项。
- Spring 的
@Component、@RequestMapping:容器需扫描并注册 Bean 或绑定路由,必须在类加载后读取 - 自定义 AOP 切面(如
@Log、@Trace):每次方法执行前动态提取注解参数,反射调用不可避免 - 配置驱动行为(如
@EnableFeature("pay")):值从配置中心动态加载,运行时才决定是否启用
CLASS 策略:编译后可用,运行时零负担
如果你的注解只服务于构建流程,比如生成代码、修改字节码或做静态校验,选 CLASS 就够了——它写进 .class 文件,但不进 JVM 堆,不触发任何反射开销。
- Lombok 的
@Data、@Builder:编译期生成方法,运行时完全无注解痕迹 - Dagger/Hilt 的
@Inject、@Module:APT 在编译时生成辅助类,运行时不查注解 - 内部 SDK 的资源标记或权限声明:仅供构建脚本解析,不希望暴露给 App 运行时
SOURCE 策略:最轻量,纯开发期存在
SOURCE 注解连字节码都不进,只对 .java 文件生效。适合所有不需要参与构建或运行的提示性、约束性用途。
-
@Override、@SuppressWarnings:编译器校验完就丢弃,防止误写又不占任何资源 - 团队协作注解如
@Todo、@ApiStatus.Internal:仅 IDE 高亮或生成 Javadoc,生产包里不留痕迹 - 配合 ErrorProne 或 Checkstyle 的自定义规则:静态检查通过即完成使命,无需运行时留存
别踩这些坑
默认值是 CLASS,看似安全,实则容易埋雷:有人想反射读取却发现为空;RUNTIME 注解多了,class 文件体积涨、类加载变慢、GC 压力上升,高频反射甚至拖垮吞吐量。
- 没写
@Retention?按 CLASS 处理——若后续需要反射,得显式改成 RUNTIME - 测试开关类功能(如
@EnableMock),用 SOURCE + 编译开关比 RUNTIME + if 判断更彻底 - Spring Boot 2.6+ 已默认禁用部分 RUNTIME 注解的全量扫描,改用
spring.components.index加速启动——这是对 RUNTIME 成本的直接回应
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











