Как работает JRebel?
Использует ли JRebel Javassist или какие-то манипуляции с байт-кодом? Я спрашиваю это из чистого интереса, мне на самом деле не нужно знать:)
3 ответа
JRebel использует переписывание классов (как ASM, так и Javassist) и интеграцию JVM для версии отдельных классов. Кроме того, он интегрируется с серверами приложений для перенаправления поиска классов / ресурсов и веб-серверов обратно в рабочую область. Кроме того, он интегрируется с большинством серверов приложений и сред для распространения изменений в конфигурации (метаданных или файлах). Вот и все. На разработку и поддержку уходит 10 инженеров мирового уровня, и это наш коммерческий секрет:)
Это самая близкая аргументация, которую я прочитал о том, как JRebel работает Саймоном, техническим евангелистом ZT.
Вставить содержимое здесь:
Jrebel использует инструменты и классы JVM для создания слоя косвенности. В случае загрузки класса приложения все тела методов будут перенаправлены с использованием службы перенаправления среды выполнения, как показано на рисунке 2. Эта служба управляет версиями классов и методов и загружает их, используя анонимные внутренние классы, созданные для каждой перезагружаемой версии. Давайте посмотрим на пример. Мы создадим новый класс C двумя методами:
public class C extends X {
int y = 5;
int method1(int x) {
return x + y;
}
void method2(String s) {
System.out.println(s);
}
}
Когда класс C загружается в первый раз, JRebel контролирует класс. Подпись этого класса будет такой же, но тела методов теперь перенаправляются. Загруженный класс теперь будет выглядеть примерно так:
public class C extends X {
int y = 5;
int method1(int x) {
Object[] o = new Object[1];
o[0] = x;
return Runtime.redirect(this, o, "C", "method1", "(I)I");
}
void method2(String s) {
Object[] o = new Object[1];
o[0] = s;
return Runtime.redirect(this, o, "C", "method2", "(Ljava/lang/String;)V");
}
}
Для перенаправления вызовов мы передаем вызывающий объект, параметры вызываемому методу, имя нашего класса, имя нашего метода и типы параметров и возвращаем. JRebel также загружает класс с реализациями в конкретной версии, изначально версии 0. Давайте посмотрим, как это выглядит:
public abstract class C0 {
public static int method1(C c, int x) {
int tmp1 = Runtime.getFieldValue(c, "C", "y", "I");
return x + tmp1;
}
public static void method2(C c, String s) {
PrintStream tmp1 =
Runtime.getFieldValue(
null, "java/lang/System", "out", "Ljava/io/PrintStream;");
Object[] o = new Object[1];
o[0] = s;
Runtime.redirect(tmp1, o, "java/io/PrintStream;", "println","(Ljava/lang/String;)V");
}
}
Давайте теперь скажем, что пользователь изменяет свой класс C, добавляя новый метод z() и вызывая его из method1. Класс C теперь выглядит так:
public class C {
int y = 5;
int z() {
return 10;
}
int method1(int x) {
return x + y + z();
}
...
}
В следующий раз, когда среды выполнения используют этот класс, JRebel обнаружит, что есть более новая версия, которая была скомпилирована и в файловой системе, поэтому он загружает новую версию, C1. Эта версия имеет дополнительный метод z и обновленную реализацию для method1.
public class C1 {
public static int z(C c) {
return 10;
}
public static int method1(C c, int x) {
int tmp1 = Runtime.getFieldValue(c, "C", "y", "I");
int tmp2 = Runtime.redirect(c, null, "C", "z", "(V)I");
return x + tmp1 + tmp2;
}
...
}
Вызов Runtime.redirect всегда будет перенаправлен на последнюю версию класса C, поэтому вызов new C(). Method1(10) вернет 15 до изменения кода и 25 после. Эта реализация пропускает много деталей и оптимизаций, но вы поняли идею.
Источник: http://zeroturnaround.com/rebellabs/why-hotswap-wasnt-good-enough-in-2001-and-still-isnt-today/
Отличная статья на эту тему Дейва Бута. Перезагрузка классов Java: HotSwap и JRebel - за кулисами.