DéveloppeurWeb.Com
    DéveloppeurWeb.Com
    • Agile Zone
    • AI Zone
    • Cloud Zone
    • Database Zone
    • DevOps Zone
    • Integration Zone
    • Web Dev Zone
    DéveloppeurWeb.Com
    Home»Uncategorized»Classes cachées en Java 15
    Uncategorized

    Classes cachées en Java 15

    février 5, 2023
    Classes cachées en Java 15
    Share
    Facebook Twitter Pinterest Reddit WhatsApp Email

    Dans la section précédente, nous avons créé dynamiquement une nouvelle classe et chargé la nouvelle classe masquée. Le chargement a été effectué à l’aide d’objets de recherche que nous avons acquis à partir du MethodHandles classe. Dans cette section, nous verrons comment nous pouvons faire la même chose en appelant l’API fluide de SourceBuddy.

    Le code créant une classe disant bonjour est le suivant :

    1.         final var hello = Compiler.java()
    2.                 .from(CODE1.replaceAll("\\.Hello", ".PublicHello")).hidden()
    3.                 .compile().load().newInstance(PublicHello.class);
    4.         hello.hello();

    Dans ce code, nous avons remplacé l’interface de Hello pour PublicHelloque vous pouvez deviner :

    1.     public interface PublicHello {
    2.         void hello();
    3.     }

    Il est essentiellement le même que l’interface précédente mais est public. Le processus est beaucoup plus simple qu’auparavant. Nous spécifions le code source; nous déclarons qu’il s’agit d’une classe cachée appelant hidden()et nous compilons, chargeons et demandons un cast d’instance pour PublicHello.

    Si nous voulons utiliser l’interface package-private, comme (ne remplaçant pas Hello pour PublicHello):

    1.         Assertions.assertThrows(IllegalAccessError.class, () ->
    2.                 Compiler.java().from(CODE1).hidden().compile().load().newInstance(PublicHello.class));

    java.lang.IllegalAccessError: class com.javax0.blog.hiddenclasses.MySpecialClass/0x00000008011b1c00 cannot access its superinterface com.javax0.blog.hiddenclasses.TestHiddenClassLoader$Hello (com.javax0.blog.hiddenclasses.MySpecialClass/0x00000008011b1c00 is in unnamed module of loader com.javax0.sourcebuddy.ByteClassLoader @4e5ed836; com.javax0.blog.hiddenclasses.TestHiddenClassLoader$Hello is in unnamed module of loader 'app')

    La raison est expliquée clairement dans le message d’erreur. L’interface et la classe qui l’implémente se trouvent dans deux modules différents. Les deux sont des modules sans nom, mais ils ne sont pas identiques. En Java, à partir de Java 9, il y a des modules, et lorsque l’application n’utilise pas de modules, elle crée essentiellement des pseudo modules en y mettant les classes. Les classes JDK sont toujours dans des modules, comme java.base.

    La création de classe cachée, telle que créée ci-dessus, utilise un chargeur de classe séparé pour charger la classe Java écrite dynamiquement. Le chargeur de classe séparé charge les classes dans son module. Le code de différents modules ne peut pas voir les classes d’autres modules à moins qu’elles ne soient publiques.

    Bien que SourceBuddy fasse une petite astuce pour charger une classe cachée, il ne peut pas contourner cette restriction.

    Le chargement d’une classe masquée nécessite un objet de recherche. L’application fournit généralement cet objet. Les appels ci-dessus ne spécifient aucun objet de recherche, mais SourceBuddy en a toujours besoin. Pour en avoir un, il en crée un. L’objet de recherche se souvient de la classe appelée MethodHandles.lookup() pour en créer un. Lors du chargement d’une classe masquée, il est nécessaire que l’objet de recherche « appartienne » au package de la classe. L’objet de recherche a été créé, l’appelant à partir d’une classe, qui est dans ce paquet. L’objet de recherche « appartiendra » à cette classe et donc au package de la classe.

    Pour avoir un objet de recherche qui provient d’une classe d’un package spécifique, nous avons besoin d’une classe dans ce package qui peut nous en donner un. S’il n’y en a pas dans le code, il faut en créer un dynamiquement. SourceBuddy fait exactement cela. Il crée le code source Java pour la classe, le compile et le charge, l’instancie et appelle le Supplier<MethodHandles.Lookup> défini get() méthode que la classe implémente.

    C’est une sorte d’astuce qui semble violer le contrôle d’accès intégré à Java. Nous semblons obtenir une nouvelle classe cachée dans un package qui n’était pas préparé pour cela. Un package est protégé des accès externes en Java (trivial). Seuls les membres et les classes publics et protégés peuvent être utilisés en dehors du package. Le package est accessible en utilisant la réflexion depuis l’extérieur, mais uniquement dans le même module, ou le module doit être ouvert explicitement. De même, un objet chargé à l’aide d’un objet de recherche doit se trouver dans le même package et accéder aux membres internes du package et ainsi de suite si une classe du package a fourni cette recherche.

    Comme nous pouvons le voir dans le message d’erreur, il semble que ce ne soit que le package. En réalité, la nouvelle classe cachée se trouve dans un package portant le même nom mais dans un module différent.

    Si vous souhaitez avoir une classe masquée dans le même package et pas seulement un package portant le même nom, vous avez besoin d’un objet de recherche de ce package.

    Dans notre exemple, c’est simple. Notre Hello interface est dans le même package que le code de test afin que nous puissions créer nous-mêmes l’objet de recherche :

    1.         final var hi = Compiler.java().from(CODE1).hidden(MethodHandles.lookup()).compile()
    2.                 .load().newInstance(Hello.class);
    3.         hi.hello();

    L’accès à un objet de recherche peut être un peu plus complexe dans des exemples réels. Lorsque le code appelant SourceBuddy se trouve dans un package différent de celui généré, la création de l’objet de recherche ne peut pas se trouver dans le code appelant SourceBuddy.

    Dans l’exemple suivant, nous verrons comment cela sera fait.

    Nous avons une classe OuterClass dans le paquet com.javax0.blog.hiddenclasses.otherpackage.

     1. package com.javax0.blog.hiddenclasses.otherpackage;
     2.
     3. import java.lang.invoke.MethodHandles;
     4.
     5. public class OuterClass {
     6.
    14.     public static MethodHandles.Lookup lookup() {
    15.         return MethodHandles.lookup();
    16.     }
    17. }

    Note

    Certaines lignes sont ignorées de la classe. Nous les utiliserons plus tard.

    Cette classe a une méthode, lookup(). Il crée un objet de recherche et le renvoie. Nous aurons un objet de recherche approprié si nous appelons cette méthode à partir de notre code. Notez que cette classe est dans un package différent et n’est pas la même que notre code de test. Notre code de test est en com.javax0.blog.hiddenclasseset OuterClass est un paquet plus profond. Essentiellement dans un emballage différent.

    Nous avons aussi une autre classe pour la démonstration.

    1. package com.javax0.blog.hiddenclasses.otherpackage;
    2.
    3. class MyPackagePrivateClass {
    4.
    5.     void sayHello(){
    6.         System.out.println("Hello from package private.");
    7.     }
    8.
    9. }

    Il s’agit d’une classe package-private contenant une méthode package-private. Si nous créons dynamiquement une classe cachée, comme dans l’exemple suivant :

     1.         final var hidden = Compiler.java().from("""
     2.                 package com.javax0.blog.hiddenclasses.otherpackage;
     3.
     4.                 public class AnyName_ItWillBeDropped_Anyway {
     5.                     public void hi(){
     6.                         new MyPackagePrivateClass().sayHello();
     7.                     }
     8.                 }""").hidden(OuterClass.lookup()).compile().load().newInstance();
     9.         final var hi = hidden.getClass().getDeclaredMethod("hi");
    10.         hi.invoke(hidden);

    Il y a un sujet que nous n’avons pas abordé. C’est comment créer un compagnon de nid.

    Lorsque vous avez un fichier de classe binaire, vous pouvez le charger en tant que compagnon d’imbrication dans une classe qui fournit un objet de recherche. La JVM ne se soucie pas de la façon dont cette classe a été créée. Lorsque nous compilons des sources Java, nous n’avons qu’une seule possibilité. La classe doit être une classe interne.

    Lorsque vous utilisez SourceBuddy, vous devez fournir votre code source en tant que classe interne à celle avec laquelle vous souhaitez que le caché soit imbriqué. Le code source et la classe étaient déjà fournis lorsque vous avez compilé votre code. Il n’est pas possible d’insérer dans CE code source une nouvelle classe interne. Nous devons tromper le compilateur.

    Nous fournissons une classe portant le même nom que celle que nous voulons insérer ultérieurement dans notre classe interne. Lorsque la compilation est terminée, nous avons également la classe externe et la classe interne. Nous disons à la classe loading d’oublier l’extérieur et de ne charger que l’intérieur, caché.

    C’est ce que nous allons faire. Cette fois, nous affichons ici toute la classe externe que nous utilisons pour la démonstration, y compris les lignes sautées.

     1. package com.javax0.blog.hiddenclasses.otherpackage;
     2.
     3. import java.lang.invoke.MethodHandles;
     4.
     5. public class OuterClass {
     6.
     7.     // skip lines
     8.     private int z = 55;
     9.
    10.     public int getZ() {
    11.         return z;
    12.     }
    13.     // end skip
    14.     public static MethodHandles.Lookup lookup() {
    15.         return MethodHandles.lookup();
    16.     }
    17. }

    Comme vous le verrez, il a un champ privé et un getter pour tester efficacement la valeur modifiée. Il a également le mentionné ci-dessus lookup() méthode. Le code créant dynamiquement une classe interne est le suivant :

     1.         final var inner = Compiler.java().from("""
     2.                         package com.javax0.blog.hiddenclasses.otherpackage;
     3.
     4.                         public class OuterClass
     5.                                                 {
     6.                             private int z;
     7.
     8.                             public static class StaticInner {
     9.                                public OuterClass a(){
    10.                                  final var outer = new OuterClass();
    11.                                  outer.z++;
    12.                                  return outer;
    13.                                }
    14.                             }
    15.
    16.                         }""").nest(MethodHandles.Lookup.ClassOption.NESTMATE).compile().load()
    17.                ...
    Share. Facebook Twitter Pinterest LinkedIn WhatsApp Reddit Email
    Add A Comment

    Leave A Reply Cancel Reply

    Catégories

    • Politique de cookies
    • Politique de confidentialité
    • CONTACT
    • Politique du DMCA
    • CONDITIONS D’UTILISATION
    • Avertissement
    © 2023 DéveloppeurWeb.Com.

    Type above and press Enter to search. Press Esc to cancel.