переименование поля с помощью javassist во время выполнения в предварительном основном методе (инструментарий Java)

Я хочу переименовать поле внутри класса Java во время выполнения. Кроме того, любой метод, который обращается к этому полю, будь то чтение или запись; Мне нужно изменить его, чтобы использовать новое имя вместо старого имени....

Все это будет сделано внутри метода pre-main...

В качестве примера, учитывая следующий код:

public class Class1
{
    String strCompany;

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

В приведенном выше классе мне нужно изменить поле «strCompany» на «strCompany2», кроме того, мне нужен метод Test для использования нового имени вместо старого....

изменить имя поля можно с помощью метода setName из класса ctField, но как я могу изменить тело метода, чтобы использовать новое имя.


person ManKeer    schedule 04.11.2014    source источник
comment
Взгляните на API уровня байтового кода Javassist. Однако я бы порекомендовал вам использовать для этого такой инструмент, как ASM, который, вероятно, более подходит.   -  person Rafael Winterhalter    schedule 05.11.2014
comment
@raphw на самом деле javassist позволяет вам делать это без использования низкоуровневого API.   -  person pabrantes    schedule 28.11.2014


Ответы (1)


Ну, я опоздал с ответом, но я надеюсь, что вы все еще найдете его полезным (или, по крайней мере, кому-то еще нужны подобные вещи).

Несмотря на то, что вы можете использовать API-интерфейс байт-кода низкого уровня, такой как Raphw, предложенный в комментарии, 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 ссылками на поле с именем strCompany2 в ctClass (которое оказывается тем же класс). Имейте в виду, что это необходимо сделать до переименования поля в strCompany2.

В конце этого запуска у вас будет новый класс Class1 в папке InjectedClasses, готовый использовать strCompany2 вместо strCompany. :-)

Примечание

Имейте в виду, что на самом деле CodeConverter создает новую запись в пуле констант класса и перенаправляет все ссылки из записи, касающейся старого поля, в ту, которая определяет «новое» (читай переименованное) поле.

Итак, в примере 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               <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 кадра, которые представляют определение нового поля. Обычно это не проблема, но тем не менее я предпочитаю упоминать об этом заранее.

person pabrantes    schedule 28.11.2014
comment
Большое спасибо за этот подробный ответ, На самом деле я много раз пытался использовать кодоконвертер, единственная ошибка в том, что я изменил имя поля перед инструментированием. - person ManKeer; 30.11.2014
comment
pabrantes, что, если я сделаю все вышеперечисленное внутри метода преобразования в классе Transformer, возможно ли это, поскольку я вызываю метод инструмента внутри метода преобразования.... - person ManKeer; 01.12.2014
comment
@ManKeer: обычно Transformer использует низкоуровневый API, изменяющий CodeAttribute через CodeIterator. Тем не менее, поскольку у вас также есть переменные более высокого уровня (такие как CtClass и CtField), вам может сойти с рук это... Но по моему личному опыту я бы рекомендовал использовать трансформаторы только при работе на уровне кода операции. - person pabrantes; 01.12.2014