Javassist добавить метод и вызвать

Я застрял с javassist. Я добавил новый метод в свой класс object во время выполнения.

мой класс объекта:

package tmp3;

public class Car {
    public Car(){}
}

мой тестовый класс:

package tmp3;

import java.lang.reflect.Method;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;

public class TestMain {
    public static void main(String[] args) {
        try {

            CtClass ctclass = ClassPool.getDefault().get("tmp3.Car");
            CtMethod newmethod = CtNewMethod.make("public void testPrint() { System.out.println("test ok"); }",ctclass);
            ctclass.addMethod(newmethod);
            ctclass.writeFile();

            for(Method me: ctclass.toClass().getDeclaredMethods()){ //test print, ok
                System.out.println(me.getName());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

но после этого момента, я не знаю, как назвать(вызвать) его. Я читал, что javassist не имеет возможности вызывать методы. Тогда как я могу вызвать метод, который я добавил с javassist?

Я пробовал много вещей за два дня, но не имел успеха. Не могли бы вы помочь мне с это?

2 ответов


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

вы использовали Javassist, структуру модификатора байт-кода, чтобы сконструировать скомпилированный класс для добавления большего байт-кода, представляющего новый метод. Вы можете иметь один из двух сценариев:

Сценарий 1: код, который компилируется вместе с вашим автомобилем класс перед инъекцией

в этом случае, когда ваш код компилируется, компилятор Java знает только Car интерфейс без каких-либо инъекций. Так ты не могу просто вызовите введенный метод напрямую, например:

 Car car = new Car();
 car.testPrint();

вы должны сделать это путем отражения, как @Scorpion правильно прокомментировал:

 Car car = new Car();
 Method method = car.getClass().getMethod("testPrint", new Class[]{});
 method.invoke(car,new Object[]{});

но это не единственный способ...

Сценарий 2: код, который использует ваш скомпилированный и введенный Класс!--15-->

если вы скомпилировали Car класс, впрысните его и после написать код против скомпилированного класса (например, имея Car class в файле jar) вы сможете вызвать свой введенный метод, как если бы это был любой другой обычный метод.

выполнить следующее упражнение:

  1. компилировать Car класс
  2. запустите TestMain, который будет делать инъекцию
  3. создать другой проект в IDE и добавьте в путь к классам этого проекта каталог с введенным классом или создайте банку только с введенным классом и добавьте эту банку в classpath
  4. создайте класс в новом проекте, который создает новый Car экземпляр, обратите внимание, что теперь вы можете вызвать метод testPrint без каких-либо проблем.

несколько вещей, которые вы должны держать внимание:

  • если ты перезаписи исходного класса с вводят class, вы можете получить недопустимый класс, в результате чего java.lang.ClassFormatError С сообщением об ошибке, говорящее, что у вас есть усеченный файл класса. Это происходит, если Javassist не загрузил весь байт-код в память и попытался записать и прочитать в тот же файл класса и из него, что приводит к полному беспорядку. Чтобы избежать этого, вы можете либо написать другой путь, либо загрузить весь байт-код в память перед записью файла (используйте toByteCode() С CtClass) .
  • если у вас есть два файлы классов, один с введенным кодом и один с исходным кодом, не забудьте есть только один в вашем classpath.

используйте отражение java для вызова его, javassist для модификации и загрузки класса ... затем Java reflection для его запуска (invoke ...).