java反射性能优化的关键在于按需启用反射,首要措施是合理设置注解的@retention策略:source用于编译期,class用于构建期,仅runtime支持运行时反射读取;必须确保aop、mvc、校验等依赖注解的功能使用runtime,避免误设导致运行时异常;对runtime注解应缓存反射结果,优先复用spring的annotationutils,并考虑编译期生成、构建期处理或启动时静态注册等非反射替代方案。

Java反射性能优化的关键,不在于“少用反射”,而在于“让反射只在真正需要时才发生”。注解的 @Retention 策略,正是控制这种“需要与否”的第一道开关——它决定了反射读取注解这件事是否可能发生,以及是否值得被优化。
RUNTIME 注解:反射读取的唯一入口
只有 RetentionPolicy.RUNTIME 的注解才能通过 Class.getAnnotation()、Method.getAnnotation() 等反射 API 获取。这意味着:如果一个注解设为 SOURCE 或 CLASS,无论你写多少反射代码,都拿不到它——不是性能低,是根本不存在。
- Spring 的
@Transactional、@RequestMapping必须是 RUNTIME,否则 AOP 和 MVC 路由无法在运行时生效 - 自定义校验注解(如
@NotBlank)也必须是 RUNTIME,否则Validator.validate()无法读取规则 - 若误将本该 RUNTIME 的注解设为 CLASS,程序编译通过但运行时报
NullPointerException或逻辑失效,排查成本高
避免无谓反射:从 Retention 开始做减法
很多性能问题源于“反射调用本身没问题,但反射调用得太多、太频繁、且读的是不需要的信息”。而最轻量的优化,就是不让反射去读它不该读的东西。
- 把仅用于 IDE 提示或编译检查的注解(如自定义
@ApiNote、@MockOnly)设为 SOURCE,彻底排除反射路径 - 构建期字节码增强(如用 Byte Buddy 注入监控逻辑)所需的注解,用 CLASS 即可,既保留元数据又不进 JVM 元空间
- 禁止将 Lombok 类注解(如
@Data)设为 RUNTIME——它们本就不该在运行时存在,设错会增大 class 文件、拖慢类加载
反射缓存:针对 RUNTIME 注解的必选优化
一旦确定必须用 RUNTIME,反射开销就真实存在。方法对象、注解实例等高频访问项,必须缓存。
- 对同一 Class 或 Method,重复调用
getAnnotation()是低效的;应使用ConcurrentHashMap缓存解析结果,键为Class + AnnotationType - Spring 框架内部大量使用
AnnotationUtils和AnnotatedElementUtils,其底层已内置注解继承合并与缓存逻辑,建议优先复用而非手写 - 若需自定义注解处理器,推荐配合
java.lang.reflect.Proxy或 CGLIB 在代理层预解析注解,避免每次方法调用都触发反射
替代方案:非反射路径更高效
不是所有元数据都需要靠反射读取。有些场景,换一种策略反而更稳更快。
- 编译期生成代码:Lombok、MapStruct 都在 javac 阶段完成逻辑注入,运行时零反射、零开销
- 构建期处理:Gradle 插件扫描 CLASS 级注解生成配置文件或注册表,运行时直接查 Map,不碰反射
- 静态注册机制:框架启动时遍历所有类,集中解析 RUNTIME 注解并缓存到内存结构中,后续业务逻辑只查缓存,不调反射 API
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











