核心优化是将动态查找前移至启动期或编译期:启动时预扫描并缓存field与注解实例,用concurrenthashmap存储;调用field.setaccessible(true)一次后直接get;用methodhandle替代invoke,耗时从120ns降至60ns;终极方案是注解处理器编译期生成零反射访问器。

注解与反射组合在高并发场景下容易暴露性能瓶颈,核心问题不在“用不用”,而在于“怎么用”——频繁调用 getDeclaredAnnotations()、重复扫描字段、每次访问都做安全检查和类型解析,会让单次操作从纳秒级拖到百纳秒甚至微秒级。关键优化方向是:把运行时的动态查找,尽可能前移到启动期或编译期,运行时只做轻量级缓存访问。
启动期预扫描 + 热点字段缓存
避免每次读属性都调用 getClass().getDeclaredField() 和 getAnnotation()。应在应用初始化阶段,一次性扫描所有带自定义注解(如 @HotField、@JsonKey)的类与字段,并缓存 Field 实例及对应注解实例。
- 使用
ConcurrentHashMap<string field></string>缓存字段引用,key 为className.fieldName - 调用
field.setAccessible(true)一次开启权限,后续直接field.get(obj) - 注解本身也缓存,比如
Map<field myannotation></field>,避免反复反射读取
用 MethodHandle 替代传统反射调用
相比 Method.invoke(),MethodHandle 更接近 JVM 原生调用机制,跳过部分安全校验和栈帧构建,实测平均耗时可从 120ns 降至 60ns 左右。
- 在首次获取注解相关方法(如自定义注解的
value()方法)时,用MethodHandles.lookup().unreflect(method)构建句柄 - 将句柄存入静态缓存,后续直接
handle.invoke(obj) - 注意:
MethodHandle是线程安全的,无需额外同步
编译期生成替代运行时反射
真正消除开销的终极方式,是不让反射出现在运行时。通过注解处理器(Annotation Processor),在 javac 编译阶段就分析源码结构,为每个标注了 @JsonAdapter 或 @BindField 的类,生成专用的访问器类(如 User$$TypeAdapter)。
- 生成代码中全是直接字段访问(
user.name)、构造器调用、类型转换,零反射 - Gson、Moshi、Kotlin KAPT 生态已有成熟实践,序列化/反序列化性能可提升 15 倍
- 适用于字段结构稳定、不依赖运行时动态类型的场景
按需延迟解析 + 注解元数据快照
不是所有注解都需要立刻解析。对低频或条件触发的注解(如日志切面、权限校验),可采用延迟求值策略:
- 仅在 AOP 切入点首次执行时,才解析目标方法上的
@PreAuthorize或@LogTrace - 解析结果(如 SpEL 表达式树、权限规则列表)缓存到方法句柄上,后续复用
- 结合
java.lang.ref.SoftReference管理大对象缓存,避免内存压力











