Java反射高频调用慢的根源在于每次调用重复解析与安全检查,优化核心是提前固化路径:缓存Method并预设setAccessible(true),用ConcurrentHashMap存储,键为Class+方法名+参数类型;优先采用MethodHandle替代invoke(),其开销接近直接调用且支持JIT优化;模块化环境下必须在module-info.java中opens对应包,否则setAccessible失效;性能敏感场景应避免反射,选用编译期方案或框架封装(如Spring ReflectionUtils)。

Java 反射高频调用慢,不是“用了反射就一定慢”,而是默认用法把大量开销堆在每次调用上。核心解法就一条:把运行时不确定的动作,提前固化成确定路径。
缓存 Method + 立即 setAccessible(true)
这是见效最快、成本最低的优化点。getDeclaredMethod() 每次都重新解析字节码、校验权限;不设 setAccessible(true),每次 invoke 都走完整安全检查链——实测多耗 5%–10% 开销,高频下放大成倍。
- 在类加载或首次获取时调用 method.setAccessible(true),之后所有 invoke 都跳过访问检查
- 用 ConcurrentHashMap 缓存 Method,键推荐 Class + 方法名 + 参数类型数组(别用 toString())
- public 方法若用 getDeclaredMethod 获取,也必须 setAccessible(true) —— 这是很多人忽略的关键点
用 MethodHandle 替代 invoke()
MethodHandle 是 JVM 原生支持的轻量调用机制,不走反射通用入口,JIT 能内联、能优化,invokeExact() 的开销接近直接调用。
- 通过 MethodHandles.lookup().findVirtual() 或 findStatic() 获取句柄
- 调用时严格匹配参数和返回类型,不自动装箱/拆箱,也不做类型擦除适配
- JDK 9+ 模块环境下,需在 module-info.java 中 opens 对应包,否则抛 IllegalAccessException
模块系统下绕不开的 opens 配置
JDK 9 后,setAccessible(true) 在模块隔离下失效是常态,不是 bug。目标包未显式开放,再设 true 也白搭。
立即学习“Java免费学习笔记(深入)”;
- 生产环境应在 module-info.java 明确声明:opens "com.yourpkg.internal" to your.module
- 调试阶段可用启动参数临时放开:--add-opens java.base/java.lang=ALL-UNNAMED
- 上线前务必验证句柄是否真能调用,避免静默失败
高频场景优先放弃反射,选编译期方案
反射是兜底手段,不是性能敏感路径的首选。框架内部已做好优化,比手写反射更稳更快。
- 字段读写优先用自动生成的 getter/setter,或统一抽象为接口
- 序列化、ORM、DI 等场景,复用 Spring 的 ReflectionUtils 或 ResolvableType
- 避免依赖 Introspector 查找 accessor——它仍是反射调用,只是封装层略快,本质没变


















