Recherche…


Remarques

Un classloader est une classe dont le but principal est d'assurer la médiation de l'emplacement et du chargement des classes utilisées par une application. Un chargeur de classe peut également rechercher et charger des ressources .

Les classes de chargeur de classe standard peuvent charger des classes et des ressources à partir de répertoires du système de fichiers et de fichiers JAR et ZIP. Ils peuvent également télécharger et mettre en cache des fichiers JAR et ZIP à partir d'un serveur distant.

Les chargeurs de classe sont normalement enchaînés, de sorte que la JVM essaye de charger les classes des bibliothèques de classes standard de préférence aux sources fournies par l'application. Les chargeurs de classes personnalisés permettent au programmeur de modifier cela. Le peut également faire des choses telles que le décryptage des fichiers bytecode et la modification bytecode.

Instancier et utiliser un chargeur de classe

Cet exemple de base montre comment une application peut instancier un chargeur de classe et l'utiliser pour charger dynamiquement une classe.

URL[] urls = new URL[] {new URL("file:/home/me/extras.jar")};
Classloader loader = new URLClassLoader(urls);
Class<?> myObjectClass = loader.findClass("com.example.MyObject");

Le classloader créé dans cet exemple aura le classloader par défaut en tant que parent, et essaiera d'abord de trouver une classe dans le classloader parent avant de chercher dans "extra.jar". Si la classe demandée a déjà été chargée, l'appel findClass renverra la référence à la classe précédemment chargée.

L'appel findClass peut échouer de différentes manières. Les plus courants sont:

  • Si la classe nommée ne peut pas être trouvée, l'appel avec jeter ClassNotFoundException .
  • Si la classe nommée dépend d'une autre classe introuvable, l'appel lancera NoClassDefFoundError .

Implémentation d'un classLoader personnalisé

Chaque chargeur personnalisé doit étendre directement ou indirectement la classe java.lang.ClassLoader . Les principaux points d'extension sont les méthodes suivantes:

  • findClass(String) - surcharge cette méthode si votre chargeur de classe suit le modèle de délégation standard pour le chargement de classe.
  • loadClass(String, boolean) - surcharge cette méthode pour implémenter un autre modèle de délégation.
  • findResource et findResources - surchargent ces méthodes pour personnaliser le chargement des ressources.

Les méthodes defineClass qui sont chargées de charger réellement la classe à partir d'un tableau d'octets sont final pour éviter la surcharge. Tout comportement personnalisé doit être effectué avant d'appeler defineClass .

Voici un simple qui charge une classe spécifique à partir d'un tableau d'octets:

public class ByteArrayClassLoader extends ClassLoader {
    private String classname;
    private byte[] classfile;

    public ByteArrayClassLoader(String classname, byte[] classfile) {
        this.classname = classname;
        this.classfile = classfile.clone();
    }

    @Override
    protected Class findClass(String classname) throws ClassNotFoundException {
        if (classname.equals(this.classname)) {
            return defineClass(classname, classfile, 0, classfile.length);
        } else {
            throw new ClassNotFoundException(classname);
        }
    }
}

Comme nous avons uniquement remplacé la méthode findClass , ce chargeur de classe personnalisé se comportera comme suit lorsque loadClass est appelé.

  1. La méthode loadClass du chargeur de loadClass appelle findLoadedClass pour voir si une classe portant ce nom a déjà été chargée par ce chargeur de classe. Si cela réussit, l'objet Class résultant est renvoyé au demandeur.
  2. La méthode loadClass délègue ensuite au chargeur de classes parent en appelant son appel loadClass . Si le parent peut traiter la demande, il retournera un objet Class qui est ensuite renvoyé au demandeur.
  3. Si le chargeur de classe parent ne peut pas charger la classe, findClass appelle alors notre méthode findClass substitution, en transmettant le nom de la classe à charger.
  4. Si le nom demandé correspond à this.classname , nous appelons defineClass pour charger la classe réelle à partir du this.classfile octets this.classfile . L'objet Class résultant est ensuite renvoyé.
  5. Si le nom ne correspond pas, nous lançons ClassNotFoundException .

Chargement d'un fichier .class externe

Pour charger une classe, il faut d'abord la définir. La classe est définie par le ClassLoader . Il n'y a qu'un seul problème, Oracle n'a pas écrit le ClassLoader du ClassLoader avec cette fonctionnalité disponible. Pour définir la classe, nous devrons accéder à une méthode appelée defineClass() qui est une méthode privée du ClassLoader .

Pour y accéder, nous allons créer une nouvelle classe, ByteClassLoader , et l'étendre à ClassLoader . Maintenant que nous avons étendu notre classe à ClassLoader , nous pouvons accéder aux méthodes privées du ClassLoader . Pour rendre defineClass() disponible, nous allons créer une nouvelle méthode qui agira comme un miroir pour la defineClass() private defineClass() . Pour appeler la méthode privée , nous aurons besoin du nom de la classe, le name , les octets de classe, classBytes , crédits compensatoires du premier octet, qui sera 0 parce que classBytes les données commence à classBytes[0] , et le décalage de dernier octet, qui sera classBytes.lenght car il représente la taille des données, qui sera le dernier décalage.

public class ByteClassLoader extends ClassLoader {

    public Class<?> defineClass(String name, byte[] classBytes) {
        return defineClass(name, classBytes, 0, classBytes.length);
    }

}

Maintenant, nous avons une defineClass() publique defineClass() . Il peut être appelé en passant le nom de la classe et les octets de la classe comme arguments.

Disons que nous avons une classe nommée MyClass dans le paquet stackoverflow ...

Pour appeler la méthode, nous avons besoin des octets de classe, nous créons donc un objet Path représentant notre chemin de classe en utilisant la méthode Paths.get() et en passant le chemin de la classe binaire en argument. Maintenant, nous pouvons obtenir les octets de classe avec Files.readAllBytes(path) . Nous créons donc une instance de ByteClassLoader et utilisons la méthode que nous avons créée, defineClass() . Nous avons déjà les octets de classe, mais pour appeler notre méthode, nous avons également besoin du nom de classe qui est donné par le nom du paquet (point), le nom canonique de la classe, dans ce cas stackoverflow.MyClass .

Path path = Paths.get("MyClass.class");

ByteClassLoader loader = new ByteClassLoader();
loader.defineClass("stackoverflow.MyClass", Files.readAllBytes(path);

Remarque : La méthode defineClass() renvoie un objet Class<?> . Vous pouvez le sauvegarder si vous le souhaitez.

Pour charger la classe, il suffit d'appeler loadClass() et de passer le nom de la classe. Cette méthode peut lancer une ClassNotFoundException ; nous devons donc utiliser un bloc try cath

try{
    loader.loadClass("stackoverflow.MyClass");
} catch(ClassNotFoundException e){
    e.printStackTrace();
}


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow