问题
I want to rename a field inside a java class at runtime. In addition, Any method that access that field ;wether it's read or write; I need it to be modified to use the new name instead of the old name....
All this will be done inside the pre-main method...
As an Exmaple, given the following code:
public class Class1
{
String strCompany;
public String Test()
{
strCompany = "TestCompany";
return strCompany;
}
}
In the above class, I need to change the field "strCompany" to be "strCompany2", in addition I need the method Test to use the new name instead of the old name....
changing the field name can be done using the setName method from the ctField class, but how can I modify the method body to use the new name.
回答1:
Well I'm a late on the answer but I hope you still find it useful (or at least someone else needing this kind of thing).
Even though you can use the low level bytecode api like Raphw suggested in the comment javassist does allow you to do this with the higher level API (which I recomend).
The solution I'll be presenting below will change the field name and will change all references from the old field name to the new one, which it's probably what you would want anyway since you're renaming the field.
The code
Let's use your Class1 example.
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");
The access to CtField and setting its name I assume - due to your question - you already know how to do it. The trick about "rewiring" all field references is done using a CodeConverter that will replace all references to the CtField field for the references to the field named strCompany2 in ctClass (which happens to be the same class). Keep in mind that this needs to be done before renaming the field into strCompany2.
At the end of this run you'll have your newly Class1 in injectedClasses folder ready to use strCompany2 instead of strCompany. :-)
Sidenote
Keep in mind that what CodeConverter really does is create a new entry in the class Constant Pool and re-route all references from the entry regarding the old field to one that defines the "new" (read renamed) field.
So in the Class1 example, here is what happens:
Constant Pool BEFORE Injection
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 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Methodref #3.#11 // java/lang/Object."<init>":()V
#11 = NameAndType #7:#8 // "<init>":()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 AFTER injection
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 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Methodref #3.#11 // java/lang/Object."<init>":()V
#11 = NameAndType #7:#8 // "<init>":()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;
In this case, with a single field rewrite your constantPool grew 3 frames which represent the definition of the new field. Usually this is not an issue, but nevertheless I rather mention it upfront.
来源:https://stackoverflow.com/questions/26737226/renaming-a-field-using-javassist-at-runtime-in-the-pre-main-method-java-instrum