本文介绍了在运行时使用presmain方法使用javassist重命名字段(java工具)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在运行时重命名Java类中的字段.另外,任何访问该字段的方法;无论是读取还是写入;我需要对其进行修改以使用新名称而不是旧名称....

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....

所有这些操作都将在pre-main方法内完成...

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;
    }
}

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

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....

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

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.

推荐答案

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

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).

即使您可以使用注释javassist中建议的Raphw这样的低级字节码api,也允许您使用较高级的API(我建议这样做).

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.

让我们使用您的Class1示例.

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");

由于您的问题,我假设访问CtField并设置其名称-您已经知道该怎么做.使用 CodeConverter 它将替换对CtField field 的所有引用,以替换对 ctClass 中的strCompany2字段的引用(碰巧是同一类).请记住,这需要重命名为strCompany2之前完成.

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 CodeConverterthat 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.

在此运行结束时,您将在injectionedClasses文件夹中准备好新的Class1以便使用strCompany2而不是strCompany. :-)

At the end of this run you'll have your newly Class1 in injectedClasses folder ready to use strCompany2 instead of strCompany. :-)

请记住,CodeConverter的真正作用是在常量池"类中创建一个新条目,并将所有引用从与旧字段有关的条目重新路由到定义新"(读取重命名)字段的条目.

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.

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

So in the Class1 example, here is what happens:

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:
#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;

在这种情况下,用单个字段重写您的constantPool会增加3帧,代表新字段的定义.通常这不是问题,但是尽管如此,我还是要提前提起.

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.

这篇关于在运行时使用presmain方法使用javassist重命名字段(java工具)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

11-02 18:01