Lorsque vous faites du développement Java, les connaissances de base que vous devez connaître dans le mécanisme de chargeur de classe. Cet article résume brièvement le mécanisme de chargeur de classe Java. Étant donné que différentes implémentations JVM sont différentes, le contenu décrit dans cet article est limité à Hotspot JVM.
Cet article commencera par les quatre aspects de Classloader, le modèle de délégation parent fournis par JDK, comment personnaliser le chargeur de classe et les scénarios qui brisent le mécanisme de délégation parent en Java.
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 ++. Il est initialisé après le démarrage de la machine virtuelle Java. Il est principalement responsable du chargement du chemin spécifié par le paramètre% java_home% / jre / lib, -xbootclasspath et les classes dans% java_home% / jre / classes.
Diaphonie
Le chargeur Bootstrp charge l'ExtClassloadher et définit le chargeur parent de l'extclassloader sur bootstrp loder.ExtClassloader est écrit en java, en particulier, Sun.Misc.Launcher $ extclassoller. L'ExtClassloader charge principalement% java_home% / jre / lib / ext, tous les répertoires de classes sous ce chemin et les bibliothèques 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. AppClassloader est également écrit en Java. Sa classe d'implémentation est SUN.Misc.launcher $ AppClassloader. De plus, nous savons qu'il existe une méthode GetSystemClassLoader dans Classloader. Cette méthode renvoie AppClassLoader.AppClassloader est principalement responsable du chargement de la classe ou du document JAR à l'emplacement spécifié par ClassPath. Il s'agit également du chargeur de classe par défaut pour les programmes Java.
Modèle de délégation parentale
Le chargement de chargeur de classe dans Java adopte un mécanisme de délégué parent. Lors du chargement des classes à l'aide d'un mécanisme de délégué parent, les étapes suivantes sont adoptées:
Actuellement, Classloader vérifie d'abord si cette classe a été chargée à partir de la classe qu'il a déjà chargée. S'il a été chargé, il renverra directement la classe d'origine.
Chaque chargeur de classe a son propre cache de chargement. Lorsqu'une classe est chargée, elle sera placée dans le cache et peut être retournée directement lorsqu'elle sera chargée la prochaine fois.
Lorsque le cache du Classloader n'est pas trouvé, le chargeur de classe parent est délégué pour charger. Le chargeur de classe parent adopte la même stratégie. Tout d'abord, vérifiez son propre cache, puis déléguez la classe parent de la classe parent à charger, jusqu'à 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. Par exemple, comme mentionné ci-dessus, les bibliothèques de classe fournies par notre JDK Binsheng, telles que HashMap, LinkedList, etc. Une fois ces classes chargées par le chargeur de classe Bootstr, peu importe le nombre de chargeurs de classe dans votre programme, ces classes peuvent en fait être partagées, ce qui évite la confusion causée par des chargeurs de classe différents chargeant des classes différentes du même nom.
Comment personnaliser le chargeur de classe
En plus du Classloader fourni par défaut mentionné ci-dessus, Java permet également aux applications de personnaliser le Classloader. Si vous souhaitez personnaliser le Classloader, nous devons l'implémenter en héritant java.lang.classloader. Ensuite, jetons un coup d'œil à plusieurs méthodes importantes auxquelles nous devons faire attention lors de la personnalisation du chargeur de classe:
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. La mise en œuvre du mécanisme de délégation parent mentionnée ci-dessus est réellement implémentée dans cette méthode. 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, boolean résolve) lève classNotFoundException {// Tout d'abord, vérifiez si la classe a déjà été chargée C = findLoadEDClass (nom); // Vérifiez si la classe a été chargée si (c == null) {try {if (parent! = null) {c = parent.Loadclass (nom, false); Le chargeur est spécifié, le chargeur parent est délégué au chargement. } else {c = findbootstrapClass0 (name); // S'il n'y a pas de chargeur de classe parent, déléguez le chargeur bootstrap à charger}} catch (classNotFoundException e) {// Si cela n'est toujours pas trouvé, invoquez findClass dans Ordre // pour trouver la classe. c = findClass (name); // Si le chargement de la classe parent n'est pas chargé, il sera chargé via sa propre recherche. }} if (résolution) {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. Ensuite, parlons de savoir si cette méthode est mauvaise.
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. Vous pouvez lire à partir du disque ou obtenir le flux d'octets de fichiers de classe à partir du réseau. Après avoir obtenu le binaire de classe, vous pouvez le remettre à DÉFINCLASS pour un chargement ultérieur. 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. En fait, c'est aussi la seule entrée qui nous reste par JVM. Grâce à cette entrée unique, JVM garantit que le fichier de classe doit se conformer à la définition de la classe spécifiée dans 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 cassé, 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. Plus précisément, pour les classes à commencer par Java. *, L'implémentation de JVM a assuré qu'il doit être chargé 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 de réaliser le problème d'interaction des classes chargées entre différents chargeurs de classe. Les classes partagées par tous sont remises au chargeur parent pour charger, mais il y a en effet une situation en Java où les classes chargées par le chargeur de classes parent doivent utiliser des classes chargées par le chargeur pour enfants. Parlons de l'occurrence de cette situation.
Il existe une norme SPI (ServiceProviderInterface) en Java qui utilise des bibliothèques SPI, telles que JDBC, JNDI, etc. Nous savons tous que JDBC nécessite des pilotes fournis par des tiers, et le package JAR du pilote est placé dans le chemin de classe de notre application elle-même. L'API de JDBC elle-même fait partie du JDK fourni par JDK, et il a été chargé par Bootstrp. Alors, comment charger les classes d'implémentation fournies par les fabricants tiers? Java présente le concept de chargement de classe de contexte de thread. Le chargeur de classe de thread héritera du thread parent par défaut. S'il n'est pas spécifié, la valeur par défaut est le chargeur de classe système (AppClassloader). De cette façon, lorsqu'un pilote tiers est chargé, il peut être chargé via le chargeur de classe de contexte du thread.
De plus, afin d'implémenter un chargeur de classe plus flexible OSGI et certains JavaAppservers, il brise également le mécanisme de délégation parent.
Résumer
Ce qui précède est tout le contenu de cet article sur l'analyse du code de l'utilisation et de l'utilisation du mécanisme de chargeur de classe Java. J'espère que ce sera utile à tout le monde. Les amis intéressés peuvent continuer à se référer à d'autres sujets connexes sur ce site. S'il y a des lacunes, veuillez laisser un message pour le signaler. Merci vos amis pour votre soutien pour ce site!