cost - jrebel trial
¿Cómo funciona JRebel? (3)
¿JRebel usa Javassist o algún tipo de manipulación de bytecode? Estoy pidiendo esto por puro interés, en realidad no "necesito" saber :)
Este es el razonamiento más cercano que he leído sobre cómo works JRebel por Simon, ZT Technical Evangelist.
Pegando los contenidos aquí:
Jrebel instrumenta las clases de aplicación y JVM para crear una capa de indirección. En el caso de que se cargue una clase de aplicación, todos los cuerpos de método tendrán una redirección utilizando el servicio de redirección de tiempo de ejecución, como se muestra en la Figura 2. Este servicio administra y carga las versiones de clase y método utilizando clases internas anónimas creadas para cada versión que se recarga. Veamos un ejemplo. Crearemos una nueva clase C con dos métodos:
public class C extends X {
int y = 5;
int method1(int x) {
return x + y;
}
void method2(String s) {
System.out.println(s);
}
}
Cuando se carga la Clase C por primera vez, JRebel instrumenta la clase. La firma de esta clase será la misma, pero los cuerpos del método ahora están siendo redirigidos. La clase cargada ahora se verá más o menos así:
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");
}
}
Para las llamadas de redirección, pasamos el objeto que llama, los parámetros del método que se ha llamado, nuestro nombre de clase, nuestro nombre de método y los tipos de los parámetros y retorno. JRebel también carga una clase con las implementaciones en una versión específica, inicialmente la versión 0. Veamos cómo se ve:
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");
}
}
Digamos ahora que el usuario cambia su clase C al agregar un nuevo método z () e invocarlo desde method1. La clase C ahora se ve así:
public class C {
int y = 5;
int z() {
return 10;
}
int method1(int x) {
return x + y + z();
}
...
}
La próxima vez que los tiempos de ejecución usan esta clase, JRebel detecta que hay una versión más nueva que se ha compilado y en el sistema de archivos, por lo que carga la nueva versión, C1. Esta versión tiene el método adicional z y la implementación actualizada para 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;
}
...
}
La llamada Runtime.redirect siempre se enrutará a la última versión de la clase C, por lo que llamar al nuevo C (). Method1 (10) devolvería 15 antes de que el código cambie y 25 después. Esta implementación pierde muchos detalles y optimizaciones, pero se entiende la idea.
Fuente: works
Gran artículo sobre este tema por Dave Booth. Recargando clases de Java: HotSwap y JRebel - Detrás de las escenas .
JRebel usa la reescritura de clase (tanto ASM como Javassist) y la integración de JVM para versionar clases individuales. Además, se integra con los servidores de aplicaciones para redirigir las búsquedas de clases / recursos y servidores web al espacio de trabajo. Y también se integra con la mayoría de los servidores de aplicaciones y marcos para propagar los cambios a la configuración (metadatos o archivos). Eso es todo. El largo de eso requiere de 10 ingenieros de clase mundial para desarrollar y apoyar y es nuestro secreto comercial :)