核心是固化“查一次”开销,缓存高频高开销的Method、Field、注解实例和Constructor;键需精确到签名,用ConcurrentHashMap+computeIfAbsent懒加载;推荐MethodHandle替代Method.invoke提升性能。

核心是把“查一次”的开销固化下来,后续直接复用反射元数据,而不是每次调用都重新解析类结构、匹配签名、校验权限。
缓存哪些对象最值得
不是所有反射操作都需缓存,聚焦高频且高开销项:
- Method 对象:尤其是 getter/setter,每次 getDeclaredMethod() 都要遍历方法表、校验修饰符、检查继承链;缓存后跳过全部查找逻辑
- Field 对象:特别是私有字段,每次 setAccessible(true) 后再 get/set,JVM 仍会做安全检查;缓存已设为可访问的 Field 实例可省掉这部分
- 注解实例:如 method.getAnnotation(MyApi.class),每次调用都会动态生成代理、填充 memberValues、走完整访问控制流程;注解本身不可变,缓存完全安全
- Constructor:ORM 创建对象时高频调用无参构造器,缓存 Constructor 可避免重复查找与参数匹配
缓存键设计必须精确到签名
键不准会导致覆盖、错用甚至 ClassCastException。不能只用类名或方法名:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- Method 缓存键示例:
"com.example.User#setName(java.lang.String)"或User.class + "setName" + new Class[]{String.class} - Field 缓存键示例:
"com.example.User#id",必须包含声明类,避免子类同名字段冲突 - 注解缓存键推荐直接用 Method 或 Field 对象本身(它们重写了 equals/hashCode),比拼字符串更安全,也规避 ClassLoader 泄漏风险
用 ConcurrentHashMap + 懒加载实现线程安全缓存
无需第三方库,标准 JDK 就够用:
立即学习“Java免费学习笔记(深入)”;
- 用
ConcurrentHashMap存储,天然支持高并发读写 - 用
computeIfAbsent()实现懒加载:首次访问才触发反射查找,后续直接返回,避免启动期冗余初始化 - 不强引用 Method/Field —— 它们本身不持类引用,但若担心元空间压力,可对值包装
SoftReference,让 JVM 内存紧张时自动回收 - 避免静态 Map 中以 Class 为 key:容易导致 ClassLoader 无法卸载,尤其在热部署或模块化环境里
进一步提速:用 MethodHandle 替代 Method.invoke()
MethodHandle 是 JVM 原生支持的轻量调用机制,比反射 invoke() 更快且可被 JIT 内联:
- 首次获取时用
MethodHandles.lookup().unreflect(method)或unreflectGetter(field)构建句柄 - 缓存该句柄(同样按类+方法签名构造唯一键),后续调用
handle.invokeExact(obj, args) - 实测耗时可从 120ns 降至约 60ns,且无安全检查开销,线程安全,复用稳定
- 注意:MethodHandle 不跨 ClassLoader 复用,但同一 ClassLoader 下对固定签名是安全的


















