38 votes

Comment fonctionne JRebel ?

Est-ce que JRebel utilise Javassist ou une sorte de manipulation du bytecode ? Je pose cette question par pur intérêt, je n'ai pas vraiment "besoin" de savoir :)

1 votes

J'ai trouvé un bon article sur le chargement dynamique des classes ici : toptal.com/java/

49voto

Jevgeni Kabanov Points 1984

JRebel utilise la réécriture de classes (à la fois ASM et Javassist) et l'intégration JVM pour versionner les classes individuelles. De plus, il s'intègre aux serveurs d'applications pour rediriger les recherches de classes/ressources et de serveurs web vers l'espace de travail. Enfin, il s'intègre également à la plupart des serveurs d'applications et des frameworks pour propager les modifications apportées à la configuration (métadonnées ou fichiers). Voilà pour la partie courte. Pour le reste, il faut 10 ingénieurs de classe mondiale pour le développer et le supporter, et c'est notre secret commercial :)

2 votes

@Jevgeni Kabanov : Est-ce que JRebel gère le rechargement des classes finales ?

7voto

czerny Points 51

Excellent article sur ce sujet par Dave Booth. Rechargement des classes Java : HotSwap et JRebel - Dans les coulisses .

7voto

Jatin Points 8069

C'est le raisonnement le plus proche que j'ai lu sur la façon dont JRebel travaux par Simon, évangéliste technique de ZT.

Coller le contenu ici :


Jrebel instrumente les classes de l'application et de la JVM pour créer une couche d'indirection. Dans le cas où une classe d'application est chargée, tous les corps de méthode auront une redirection en utilisant le service de redirection du runtime, comme le montre la figure 2. Ce service gère et charge les versions de la classe et de la méthode en utilisant des classes internes anonymes créées pour chaque version qui est rechargée. Prenons un exemple. Nous allons créer une nouvelle classe C avec deux méthodes :

public class C extends X {
 int y = 5;
 int method1(int x) {
   return x + y;
 }
 void method2(String s) {
   System.out.println(s);
 }
}

Lorsque la classe C est chargée pour la première fois, JRebel instrumente la classe. La signature de cette classe sera la même, mais les corps de méthode sont maintenant redirigés. La classe chargée ressemblera maintenant à quelque chose comme ceci :

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");
 }
}

Aux appels de redirection, nous passons l'objet appelant, les paramètres à la méthode qui a été appelée, notre nom de classe, notre nom de méthode et les types des paramètres et du retour. JRebel charge également une classe avec les implémentations à une version spécifique, initialement la version 0. Voyons à quoi cela ressemble :

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");
 }
}

Disons maintenant que l'utilisateur modifie sa classe C en ajoutant une nouvelle méthode z() et en l'invoquant à partir de method1. La classe C ressemble maintenant à ceci :

public class C {
 int y = 5;
 int z() {
   return 10;
 }
 int method1(int x) {
   return x + y + z();
 }
 ...
}

La prochaine fois que le moteur d'exécution utilise cette classe, JRebel détecte qu'une version plus récente a été compilée et se trouve dans le système de fichiers, et charge donc la nouvelle version, C1. Cette version contient la méthode supplémentaire z et l'implémentation mise à jour de la méthode 1.

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;
 }
 ...
}

L'appel Runtime.redirect sera toujours acheminé vers la dernière version de la classe C. Ainsi, l'appel à new C().method1(10) renverra 15 avant le changement de code et 25 après. Cette implémentation manque beaucoup de détails et d'optimisations, mais vous avez compris l'idée.

Source : http://zeroturnaround.com/rebellabs/why-hotswap-wasnt-good-enough-in-2001-and-still-isnt-today/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X