在pre-main方法中使用javassist在运行时重命名字段(java instrumentation)

我想在运行时重命名java类中的字段。 此外,任何访问该字段的方法;读取或写入; 我需要将其修改为使用新名称而不是旧名称….

所有这些都将在pre-main方法中完成……

作为Exmaple,给出以下代码:

public class Class1 { String strCompany; public String Test() { strCompany = "TestCompany"; return strCompany; } } 

在上面的类中,我需要将字段“strCompany”更改为“strCompany2”,另外我需要使用方法Test来使用新名称而不是旧名称….

可以使用ctField类中的setName方法更改字段名称,但是如何修改方法体以使用新名称。

嗯,我的答案很晚,但我希望你仍然觉得它很有用(或者至少其他人需要这种东西)。

即使你可以使用像Raphw在评论中建议的低级字节码api,javassist允许你用更高级别的API(我推荐)来做这件事。

我将在下面介绍的解决方案将更改字段名称,并将所有引用从旧字段名称更改为新字段名称,这可能是您想要的,因为您重命名字段。

代码

让我们使用您的Class1示例。

  ClassPool classpool = ClassPool.getDefault(); CtClass ctClass = classpool.get(Class1.class.getName()); CtField field = ctClass.getField("strCompany"); CodeConverter codeConverter = new CodeConverter(); codeConverter.redirectFieldAccess(field, ctClass, "strCompany2"); ctClass.instrument(codeConverter); field.setName("strCompany2"); ctClass.writeFile("./injectedClasses"); 

访问CtField并设置其名称我假设 – 由于您的问题 – 您已经知道如何做到这一点。 关于“重新布线”所有字段引用的技巧是使用CodeConverter完成的,它将替换对CtField 字段的所有引用,以引用ctClass中名为strCompany2的字段(恰好是同一个类)。 请记住, 将字段重命名为strCompany2 之前 ,需要执行此操作。

在此运行结束时,您将在injectClasses文件夹中使用新的Class1,以便使用strCompany2而不是strCompany。 🙂

边注

请记住,CodeConverter真正做的是在类Constant Pool中创建一个新条目,并将关于旧字段的条目中的所有引用重新路由到定义“new”(读取重命名)字段的引用。

所以在Class1示例中,会发生以下情况:

注射前恒定池

 Constant pool: #1 = Class #2 // test/Class1 #2 = Utf8 test/Class1 #3 = Class #4 // java/lang/Object #4 = Utf8 java/lang/Object #5 = Utf8 strCompany #6 = Utf8 Ljava/lang/String; #7 = Utf8  #8 = Utf8 ()V #9 = Utf8 Code #10 = Methodref #3.#11 // java/lang/Object."":()V #11 = NameAndType #7:#8 // "":()V #12 = Utf8 LineNumberTable #13 = Utf8 LocalVariableTable #14 = Utf8 this #15 = Utf8 Ltest/Class1; #16 = Utf8 test #17 = Utf8 ()Ljava/lang/String; #18 = String #19 // TestCompany #19 = Utf8 TestCompany #20 = Fieldref #1.#21 // test/Class1.strCompany:Ljava/lang/String; #21 = NameAndType #5:#6 // strCompany:Ljava/lang/String; #22 = Utf8 SourceFile #23 = Utf8 Class1.java 

注射后恒定池

 Constant pool: #1 = Class #2 // test/Class1 #2 = Utf8 test/Class1 #3 = Class #4 // java/lang/Object #4 = Utf8 java/lang/Object #5 = Utf8 strCompany #6 = Utf8 Ljava/lang/String; #7 = Utf8  #8 = Utf8 ()V #9 = Utf8 Code #10 = Methodref #3.#11 // java/lang/Object."":()V #11 = NameAndType #7:#8 // "":()V #12 = Utf8 LineNumberTable #13 = Utf8 LocalVariableTable #14 = Utf8 this #15 = Utf8 Ltest/Class1; #16 = Utf8 test #17 = Utf8 ()Ljava/lang/String; #18 = String #19 // TestCompany #19 = Utf8 TestCompany #20 = Fieldref #1.#21 // test/Class1.strCompany:Ljava/lang/String; #21 = NameAndType #5:#6 // strCompany:Ljava/lang/String; #22 = Utf8 SourceFile #23 = Utf8 Class1.java #24 = Utf8 strCompany2 #25 = NameAndType #24:#6 // strCompany2:Ljava/lang/String; #26 = Fieldref #1.#25 //test/Class1.strCompany2:Ljava/lang/String; 

在这种情况下,通过单个字段重写,您的constantPool增长了3个帧,表示新字段的定义。 通常这不是问题,但我还是提前提到它。