如何用Byte Buddy将静态字段访问替换为生成的新方法?
问题分析与解决
问题根源
你遇到的报错核心原因是静态字段访问与实例方法调用的字节码语义不匹配:
- 读取静态字段
foo时,JVM执行GETSTATIC指令,这条指令不需要任何实例引用,直接从类的静态存储区取值 - 你要替换成的
dynamicGenerateMethod是实例方法,调用它需要INVOKEVIRTUAL指令,而这条指令必须依赖一个实例对象(也就是this引用)
当Byte Buddy尝试用实例方法调用替换静态字段读取时,当前栈帧里没有可用的实例引用,因此抛出Cannot substitute parameterless instruction异常。而非静态字段读取用GETFIELD指令,执行前栈上已经有实例引用,所以替换实例方法调用可以正常执行。
解决方法
有两种可行的修正方案:
方案1:将目标方法改为静态方法
既然要替换静态字段的访问,直接把dynamicGenerateMethod定义为静态方法,调用时就不需要实例引用:
DynamicType.Unloaded<?> make = new ByteBuddy() .redefine(A.class) .defineMethod("dynamicGenerateMethod", String.class, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC) // 添加静态修饰符 .intercept(FieldAccessor.ofField("foo")) .visit(MemberSubstitution.relaxed() .field(named("foo")) .onRead() .replaceWithMethod(named("dynamicGenerateMethod")) .on(not(named("dynamicGenerateMethod")))) .make();
方案2:保留实例方法,手动注入实例引用
如果业务上必须保留dynamicGenerateMethod为实例方法,可通过MemberSubstitution的with方法手动注入实例引用,但这种方式仅适用于在实例方法内部访问静态字段的场景(静态方法中没有this实例,依然会报错):
DynamicType.Unloaded<?> make = new ByteBuddy() .redefine(A.class) .defineMethod("dynamicGenerateMethod", String.class, Opcodes.ACC_PUBLIC) .intercept(FieldAccessor.ofField("foo")) .visit(MemberSubstitution.relaxed() .field(named("foo")) .onRead() .replaceWithMethod(named("dynamicGenerateMethod")) .on(not(named("dynamicGenerateMethod"))) .with(InvocationDelegate.toThis())) // 注入当前实例作为方法调用目标 .make();
内容的提问来源于stack exchange,提问作者Lucas
相关产品推荐
相关产品推荐

