Il existe deux types de charges de classe dans Java, l'une est définie par l'utilisateur, et l'autre est le chargeur de classe bootstrap construit en JVM.
Il existe trois types de chargeurs de classe intégrés dans JVM, à savoir Bootstrap Classloader, Extension Classloader (c'est-à-dire ExtClassloader) et System Classloader (c'est-à-dire AppClassloader).
Je ne parlerai pas de la délégation des parents lorsque JVM est chargé, il existe de nombreux articles sur Javaeye qui sont introduits ...
Vous pouvez jeter un œil à leurs constructeurs séparément, où le chargeur de classe bootstrap est écrit en c.
java.lang.classloadher
Classloader protégé (Classload Parent) {SecurityManager Sécurité = System.getSecurityManager (); true; initialisé = true;}
Ce constructeur a deux paramètres et aucun constructeur. Le constructeur avec des paramètres passe dans le chargeur parent de ce chargeur de classe, tandis que le constructeur sans paramètres traitera le chargeur de classe renvoyé par GetSystemClassOader () en tant que Code parent.
public static classloader getSystemClassoLer () {// Le chargeur de classe renvoyé est affecté ccl = getCalleclassloadher (); if (! sclset) {if (scl! = null) New illégalStateException ("invocation récursive"); OOPS = NULL; // La valeur est attribuée SCL = l.getClassloadher (); ............................ .... .....................................}} sclset = true;}} Le chargeur de classe parent ici est SCL, qui est obtenu par l.getClassloader (), getClassOader (), puis consultez le code source du lanceur:
Launcher statique privé = nouveau lanceur ();
Public Static Launcher Getlauncher () {Retournateur;} Private Classload Loder; ); AppClassloadher, c'est-à-dire que GetSystemClassOLODER renvoie AppClassloader Loder = AppClassOLD.GetAppClassLoader (EXTCL); Thread, pour éviter la confusion causée par les charges de classe dans Multithreads (je comprends moi-même, haha) // Définit également le chargeur de classe de contexte pour le thread principal. .................................................. ................................................ .. ..............} / * * Renvoie le chargeur de classe utilisé pour lancer l'application principale. À partir de cela, nous voyons que le chargeur parent d'AppClassloader est ExtClassloader, et quel est le chargeur parent d'ExtClassloadher? Regardons le constructeur ExtClassloader:
public ExtClassloader (fichier [] dirs) lance ioException {super (getExtUrls (dirs), null, factory);Son chargeur parent est vide, tandis que sa classe de parent de niveau supérieur est java.lang.classloader.
Classe Synchronisée protégé <?> LoadClass (String Name, Boolean Resolve) lance ClassNotFoundException {// Vérifiez d'abord si la classe a déjà été chargée C = FindloadDClass (name); ! = null) {// Appelez d'abord le chargeur parent. ) {// Si non trouvé, alors invoquez laClass dans l'ordre // pour trouver la classe.Ici, findbootstrapClass0 est d'appeler le bootstrap classloader, le chargeur de classe le plus principal, pour charger la classe.
Enfin, nous pouvons voir que le chargeur de classe renvoyé par GetSystemClassloader () est AppClassloader.
Analyse du mécanisme Java Classloadher
JDK Classloadher par défaut
JDK fournit les chargeurs de classe suivants par défaut
Chargeur de bootstrp
Le chargeur bootstrp est écrit en langue C ++. JRE / classes.
Diaphonie
Le chargeur Bootstrp charge le diaphonie ExtClass et définit le chargeur parent de l'ExtClassloader dans le chargeur bootstrp. Ces répertoires de classes sous les bibliothèques de chemin et de classe dans le chemin spécifié par la variable système java.ext.DIRS.
AppClassLoader
Une fois que le chargeur Bootstrp a chargé l'ExtClassLoader, le chargeur AppClassa sera chargé et le chargeur parent du chargeur AppClass est spécifié sous forme d'ExtClassLoader. AppClassload est également écrit en Java. Il s'agit d'un document JAR, qui est également le chargeur de classe par défaut pour les programmes Java.
Pour résumer, la relation entre eux peut être décrite dans la figure suivante:
Modèle de délégation parentale
Classloader Chargement dans Java adopte un mécanisme de délégué parent.
Actuellement, Classloader vérifie d'abord si cette classe a été chargée à partir de la classe qu'il a déjà chargée.
Chaque chargeur de classe a son propre cache de chargement.
Lorsque le cache Classloader n'est pas trouvé, le chargeur de classe parent est délégué pour le charger. façon de bootstrp classloader.
Lorsque tous les chargeurs de classe parent ne sont pas chargés, ils sont chargés par le chargeur de classe actuel et les mettent dans son propre cache afin qu'ils puissent être retournés directement la prochaine fois qu'il y a une demande de chargement.
En parlant de cela, vous vous demandez peut-être, pourquoi Java adopte-t-il un tel mécanisme de délégation? Pour comprendre ce problème, nous introduisons un autre concept "Espace de noms" sur Classloader, ce qui signifie que pour déterminer une certaine classe, vous avez besoin d'un nom entièrement qualifié de la classe et de charger ce chargeur de classe pour le déterminer conjointement. C'est-à-dire, même si les noms entièrement qualifiés des deux classes sont les mêmes, car différents chargeurs de classe chargent cette classe, alors c'est une classe différente dans le JVM. Après avoir compris l'espace de noms, jetons un coup d'œil au modèle du délégué. Après avoir adopté le modèle du délégué, les capacités interactives des différents chargeurs de classe sont augmentées. importe votre programme Il y a tellement de chargeurs de classe, de sorte que ces classes peuvent réellement être partagées, ce qui évite la confusion causée par différents chargeurs de classe après avoir chargé différentes classes du même nom.
Comment personnaliser le chargeur de classe
En plus du chargeur par défaut mentionné ci-dessus, Java permet également aux applications de personnaliser Classload. Nous devons prêter attention à plusieurs méthodes importantes:
1. Méthode de charge
Méthode de charge de chargement
Classe publique <?> LoadClass (nom de chaîne) lève ClassNotFoundException
Ce qui précède est la déclaration prototype de la méthode de la classe de chargement. Jetons un coup d'œil au code de cette méthode pour voir comment il met en œuvre la délégation des parents.
Méthode LoadClass Implémentation
public class <?> LoadClass (nom de chaîne) lève ClassNotFoundException {return LoadClass (name, false);}À partir de ce qui précède, nous pouvons voir que la méthode LoadClass appelle la méthode LoadCClass (nom, false), alors jetons un coup d'œil à l'implémentation d'une autre méthode LoadClass.
Class LoadClass (Nom de la chaîne, Boolean Resolve)
Classe synchronisée protégée <?> LoadClass (nom de chaîne, booléen résolution) lance classNotFoundException {// Tout d'abord, vérifiez si la classe a déjà été chargée C = findloadDClass (name); // Vérifiez si la classe a été chargée si (c = = null) {try {if (parent! = null) {c = parent.loadClass (name, false); } else {c = findbootstrapClass0 (name); // S'il n'y a pas de chargeur de classe parent, déléguer le chargeur bootstrap à charger}} catch (classNotFoundException e) {// Si cela n'est toujours pas trouvé trouver la classe. }} if (résoudre) {résolveclass (c);} return c;}Dans le code ci-dessus, j'ai ajouté des commentaires pour voir clairement comment fonctionne le mécanisme de délégation parent de LoadClass. Une chose que nous devons noter ici est que la classe publique <?> LoadClass (nom de chaîne) lève classNotFoundException n'est pas marquée comme finale, ce qui signifie que nous pouvons remplacer cette méthode, ce qui signifie que le mécanisme de délégation parent peut être brisé. De plus, nous avons remarqué qu'il existe une méthode FindClass ci-dessus.
2.FindClass
Nous vérifions le code source de java.lang.classloader et nous constatons que l'implémentation de FindClass est la suivante:
Class protégé <?> findClass (String Name) lève ClassNotFoundException {Throw new ClassNotFoundException (nom);}Nous pouvons voir que l'implémentation par défaut de cette méthode est de lancer directement des exceptions, mais en fait, cette méthode est laissée à notre application pour remplacer. L'implémentation spécifique dépend de votre logique d'implémentation. Décrivons DÉFINCLASS plus tard. OK, grâce à l'analyse ci-dessus, nous pouvons tirer les conclusions suivantes:
Lorsque nous écrivons notre propre chargeur de classe, si nous voulons suivre le mécanisme de délégation parent, nous n'avons qu'à remplacer FindClass.
3. Définition
Examinons d'abord le code source de DÉFINCLASS:
classer définitivement
Classe finale protégée <?> DefinClass (nom de la chaîne, octet [] b, int off, int len) lève classformaterror {return Deficlass (name, b, off, len, null);}À partir du code ci-dessus, nous pouvons voir que cette méthode est définie comme finale, ce qui signifie que cette méthode ne peut pas être remplacée. Le fichier doit se conformer à la définition des classes spécifiées par la spécification de la machine virtuelle Java. Cette méthode appellera enfin la méthode native pour implémenter le chargement de la classe réelle.
Ok, grâce à la description ci-dessus, réfléchissons à la question suivante:
Si nous avons écrit une classe java.lang.string nous-mêmes, pouvons-nous remplacer la classe qui appelle JDK lui-même?
La réponse est non. Nous ne pouvons pas y parvenir. Pourquoi? Je vois de nombreuses explications en ligne que le mécanisme de délégation des parents résout ce problème, mais il n'est en fait pas très précis. Parce que le mécanisme de délégation parent peut être brisé, vous pouvez écrire un Classloader pour charger la classe java.lang.string que vous avez écrite, mais vous constaterez qu'il ne se chargera pas avec succès, en particulier parce que c'est pour la classe en commençant par Java. *, JVM L'implémentation a assuré qu'elle doit être chargée par Bootstrp.
Scénarios qui ne suivent pas le "mécanisme de délégation parent" "
Ce qui précède a mentionné que le mécanisme de délégation parent est principalement pour réaliser le problème d'interaction des classes entre les différents chargeurs de classe. Chargeur de classe parent dans Java. Parlons de l'occurrence de cette situation.
Il existe une norme SPI (Interface du fournisseur de services) dans Java, qui utilise des bibliothèques SPI, telles que JDBC, JNDI, etc. Nous savons tous que JDBC a besoin de pilotes fournis par des tiers, et le package de pot de pilote est placé dans notre application elle-même. ClassPath et l'API de JDBC font partie de la disposition de JDK, et il a été chargé par Bootstrp. Java introduit le concept de chargement de contexte de thread. Pilote, il est OK de charger via le chargeur de classe de contexte du thread.
De plus, afin d'implémenter un chargeur de classe plus flexible OSGI et certains serveurs d'applications Java, il brise également le mécanisme de délégation parent.