Печать переменной экземпляра с использованием javasssist

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

package test.hib.javaassist;

import java.io.IOException;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;

public class JavaAssistTest {

    int count;


    public void doSomething1(){
        count++;
    }

    public void doSomething2(){
        count++;
    }

    public void doSomething3(){
        count++;
    }

    public void doSomething4(){
        count++;
    }

}

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

package test.hib.javaassist;

import java.io.IOException;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;

public class Main {

    public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {

        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("test.hib.javaassist.JavaAssistTest");
        CtMethod[] methods = cc.getDeclaredMethods();

        for(CtMethod method : methods){
            if(! (method.getName().equals("main"))){
                method.insertAfter("{System.out.println(count);}");
                //method.insertAfter("System.out.println($type);");
            }
        }

        cc.writeFile();

        System.out.println("Completed editting");

        JavaAssistTest test = new JavaAssistTest();
        test.doSomething1();
        test.doSomething2();
        test.doSomething3();
        test.doSomething4();

        System.out.println("Finished");

    }

}

В настоящее время печатается

Completed editting
Finished

я хочу это напечатать

Completed editting
1
2
3
4
Finished

Можете ли вы указать на ошибку в моем коде?


person Renjith    schedule 27.11.2016    source источник
comment
Я предполагаю (просто догадываюсь), что класс уже загружен, поэтому изменения в файле .class не будут отражаться во время выполнения текущей JVM. Вы можете попробовать перезагрузить класс, как описано, например. здесь: stackoverflow.com/a/3971771/281108   -  person Ismail Badawi    schedule 27.11.2016
comment
хм.... выглядит довольно сложно. Я попробовал другой способ загрузки класса. Класс testClass = Loader.getSystemClassLoader().loadClass(test.hib.javaassist.JavaAssistTest); Тест JavaAssistTest = (JavaAssistTest)testClass.newInstance(); Но никаких изменений в результатах !!   -  person Renjith    schedule 27.11.2016
comment
Я почти уверен, что класс не перезагрузится, если он уже загружен.   -  person Ismail Badawi    schedule 27.11.2016
comment
В документе загрузчика говорится: «В отличие от обычного загрузчика классов, этот загрузчик классов получает байт-код из пула классов».   -  person Renjith    schedule 27.11.2016


Ответы (1)


Javassist API здесь немного сбивает с толку. Посмотрите на реализацию writeFile. Это ярлык для writeFile("."), который сохраняет класс в каталоге вашей программы, а не в пути к классу. Вы ожидаете, что он переопределит исходный файл класса, чего этот метод не делает. Ваш системный загрузчик классов не сможет найти обновленный файл класса.

Вы можете либо вручную сохранить файл класса в нужном месте, используя writeFile(String), либо принудительно загрузить измененный класс, вызвав cc.toClass(), что вызывает загрузку измененного класса до того, как ваш загрузчик классов проверит его вручную.

person Rafael Winterhalter    schedule 28.11.2016
comment
Спасибо !! cc.toClass() работал. В конце концов, я должен использовать это в приложении, развернутом на сервере Tomcat. Но в документе Java указано, что то же самое нельзя применить к приложениям, работающим на сервере приложений. Я должен попробовать использовать «toClass (загрузчик java.lang.ClassLoader)». - person Renjith; 30.11.2016