Pour acquérir une compréhension plus profonde de Classloader, vous devez d'abord savoir à quoi est utilisé. Comme son nom l'indique, il est utilisé pour charger des fichiers de classe dans le JVM pour une utilisation par le programme. Nous savons que les programmes Java peuvent charger dynamiquement les définitions de classe, et ce mécanisme de chargement dynamique est implémenté via Classloader, afin que vous puissiez imaginer à quel point Classloader est important.
Après avoir vu cela, certains amis peuvent penser à une question, c'est-à-dire que, puisque Classloader est utilisé pour charger des classes dans le JVM, comment Classloader est-il chargé? N'est-ce pas une classe Java?
C'est vrai, il y a en effet un chargeur de classe ici qui n'est pas écrit en langue java, mais fait partie de l'implémentation JVM. Ce Classloader est Bootstrap classloader (Démarrer le chargeur de classe), ce Classloader charge l'API Java Core lorsque le JVM s'exécute pour répondre aux besoins les plus élémentaires du programme Java, y compris le Classloader défini par l'utilisateur. Le soi-disant chargeur de classe défini par l'utilisateur fait ici référence au Classloader implémenté via le programme Java. L'un est le dizaine d'ExtClass. Ce Classloader est utilisé pour charger l'API d'extension Java, c'est-à-dire la classe dans / lib / ext, et l'autre est l'AppClassloader. Ce Classloader est utilisé pour charger la classe dans le répertoire de paramètre de ClassPath sur la machine de l'utilisateur. Habituellement, sans spécifier le Classloader, les classes personnalisées du programmeur sont chargées par le Classloader.
Lors de l'exécution d'un programme, le JVM démarre et exécute Bootstrap classloader. Le Classloader charge l'API Java Core (ExtClassloader et AppClassloader sont également chargés à l'heure actuelle), puis appelle EXTCLASSLOADER pour charger l'API d'extension, et enfin AppClassloader charge la classe définie dans le répertoire de ClassPath. Il s'agit du processus de chargement le plus élémentaire d'un programme.
Ce qui précède explique brièvement le rôle de Classloader et le processus de chargement le plus élémentaire. Ensuite, nous expliquerons la façon dont Classloader se charge. Ici, nous devons parler de l'utilisation du mode du délégué parent pour le chargement des cours.
Chaque Classloader personnalisé doit hériter du Class Classloader Résumé, et chaque Classloader aura un chargeur de classe parent. Nous pouvons voir qu'il existe une méthode getParent () dans le Class Classloader de Class Abstract, qui est utilisée pour renvoyer le parent du Classloader actuel. Notez que ce parent ne fait pas référence à la classe héritée, mais un Classloader spécifié lors de l'instanciation du Classloader. Si ce parent est NULL, le parent par défaut du Classloader est le bootstrap classloader. À quoi sert ce parent?
Nous pouvons considérer cette situation. Supposons que nous ayons personnalisé un ClientDefClassLoader et que nous utilisons ce Classloader personnalisé pour charger java.lang.string, alors la chaîne sera-t-elle chargée par ce Classloader? En fait, la classe java.lang.string n'est pas chargée par le ClientDefClassloader, mais est chargée par le bootstrap classloader. Pourquoi cela se produit-il? En fait, c'est la raison du mode de délégué parent, car avant que tout chargeur de classe personnalisé charge une classe, il déléguera d'abord son chargeur de Père à charger. Il ne sera chargé d'eux-mêmes qu'après que le Père Classloader ne peut pas être chargé avec succès. Dans l'exemple ci-dessus, parce que java.lang.string est une classe appartenant à l'API Java Core, donc lors de l'utilisation de ClientDefClassloader pour le charger, le Classloader déléguera d'abord son Père Classloader pour charger. Comme mentionné ci-dessus, lorsque le parent de Classloader est NULL, le parent de Classloader est le bootstrap classloader, donc au niveau supérieur de Classloader est le bootstrap classloader, donc enfin déléguer à bootstrap lorsque le Classloader sera présent, le bootstrap classloader retournera la classe String.
Jetons un coup d'œil à un morceau de code source dans Classloader:
Classe synchronisée protégée LoadClass (nom de chaîne, Boolean Resolve) lève ClassNotFoundException {// Vérifiez d'abord si la classe spécifiée par le nom est chargée C = findloadDClass (nom); if (c == null) {try {if (parent! = null) {// Si le parent n'est pas nul, appelez le parent de chargement pour charger = parent.loadClass (name, false); } else {// parent est null, appelez bootstrapClassloader pour charger c = findbootstrapClass0 (name); }} catch (classNotFoundException e) {// Si le chargement n'est toujours pas réussi, appelez votre propre FindClass pour charger c = findClass (name); }} if (résolve) {résolveclass (c); } return c; } D'après le code ci-dessus, nous pouvons voir que le processus général de chargement d'une classe est le même que l'exemple que j'ai donné auparavant. Lorsque nous voulons implémenter une classe personnalisée, nous devons seulement implémenter la méthode FindClass.
Pourquoi utiliser ce modèle de délégation parent?
La première raison est que cela peut éviter le chargement répété. Lorsque le père a chargé la classe, il n'est pas nécessaire que l'enfant Classloader le charge à nouveau.
La deuxième raison est de considérer les facteurs de sécurité . Imaginons que si nous n'utilisons pas ce mode de délégué, nous pouvons remplacer dynamiquement les types définis dans l'API Java Core à tout moment, ce qui présentera un très gros risque de sécurité. La méthode du délégué parent peut éviter cette situation, car la chaîne est déjà chargée au démarrage, de sorte que la classe définie par l'utilisateur ne peut pas charger un chargeur de classe personnalisé.
Ce qui précède est une brève introduction au mécanisme de chargement de Classloader . Ensuite, je dois expliquer une autre classe liée à Classloader, c'est-à-dire la classe de classe. Chaque fichier de classe chargé par Classloader sera finalement référencé par le programmeur comme une instance de la classe de classe. Nous pouvons traiter la classe de classe comme un modèle de la classe ordinaire. Le JVM génère des instances correspondantes basées sur ce modèle et est finalement utilisée par le programmeur.
Nous voyons qu'il existe une méthode statique Forname dans la classe de classe. Cette méthode est la même que la méthode LoadClass dans classloader. Il est utilisé pour charger la classe, mais les deux ont des fonctions différentes.
Classe <?> LoadClass (nom de chaîne)
Classe <?> LoadClass (nom de chaîne, booléen résolution)
Nous voyons les deux déclarations de méthodes ci-dessus. Le deuxième paramètre de la deuxième méthode est utilisé pour définir l'opportunité de connecter la classe lors du chargement de la classe. Si cela est vrai, il sera connecté, sinon il ne sera pas connecté.
En parlant de connexion, je dois l'expliquer ici. Lors du chargement d'une classe par JVM, il est nécessaire de passer par trois étapes: le chargement, la connexion et l'initialisation. Le chargement signifie trouver le fichier de classe correspondant, le lire dans le JVM et l'initialiser doit être discuté, la chose la plus importante est de parler de la connexion.
La connexion est divisée en trois étapes. La première étape consiste à vérifier si la classe répond aux spécifications. La deuxième étape est la préparation. Il s'agit d'allouer de la mémoire aux variables de classe et de définir la valeur initiale par défaut. La troisième étape est l'explication. Cette étape est facultative. Selon le deuxième paramètre de la méthode de classe de charge ci-dessus, il est déterminé si une explication est nécessaire. La soi-disant explication est basée sur la définition du livre "en profondeur JVM" qui est de trouver l'entité correspondante basée sur les références de symbole dans la classe, puis de remplacer la référence du symbole par une référence directe. C'est un peu profond, haha, je ne l'expliquerai pas ici. Si vous voulez en savoir plus, veuillez lire "JVM en profondeur". Haha, si vous continuez à l'expliquer étape par étape, vous ne saurez pas quand il sera terminé.
Jetons un coup d'œil à la méthode LoadClass avec deux paramètres. Dans le document API Java, la définition de cette méthode est protégée, ce qui signifie que la méthode est protégée et la méthode que les utilisateurs doivent vraiment utiliser est celle avec un paramètre. La méthode LoadClass d'un paramètre appelle en fait la méthode avec deux paramètres, et le deuxième paramètre par défaut est faux. Par conséquent, on peut voir ici que la classe de chargement via LoadClass n'est en fait pas expliquée lors du chargement, de sorte que la classe ne sera pas initialisée. La méthode FORNAME de la classe de classe est le contraire. Lors du chargement avec Forname, la classe sera expliquée et initialisée. Forname dispose également d'une autre version de la méthode, qui peut définir l'opportunité d'initialiser et de définir Classloader. Je n'en parlerai pas plus ici.
Je me demande si l'explication de ces deux méthodes de chargement est suffisamment claire. Donnons un exemple ici. Par exemple, lors du chargement du pilote JDBC , nous utilisons le Forname au lieu de la méthode LoadClass de Classloader lors du chargement des pilotes JDBC? Nous savons que le conducteur JDBC est via le DriverManager et doit être enregistré dans le DriverManager. Si la classe de conducteur n'est pas initialisée, elle ne peut pas être enregistrée dans le DriverManager. Par conséquent, Forname doit être utilisé au lieu de la classe de chargement.
Grâce à Classloader, nous pouvons personnaliser le chargeur de classe et personnaliser la méthode de chargement dont nous avons besoin, comme le chargement à partir du réseau, le chargement à partir d'autres formats de fichiers, etc. En fait, il y a encore beaucoup de choses qui n'ont pas été mentionnées dans Classloader, telles que certaines implémentations à l'intérieur de Classloader, etc.
Grâce à cet article, l'éditeur espère que tout le monde aura une certaine compréhension du mécanisme de chargeur de classe, merci pour votre soutien!