Modèle de délégation parentale
Le concept de chargement de classe doit être considéré comme une innovation dans la langue java. Le but est de découpler le processus de chargement de la classe à partir de la machine virtuelle et d'atteindre le but de "l'obtention d'un flux d'octet binaire décrivant cette classe via le nom entièrement qualifié de la classe". Le module de code qui implémente cette fonction est le chargeur de classe. Le modèle de base du chargeur de classe est le célèbre modèle de délégation des parents. Cela semble génial, mais la logique est en fait très simple. Lorsque nous devons charger une classe, nous déterminons d'abord si la classe a été chargée. Sinon, nous déterminons s'il a été chargé par le chargeur parent. Si nous n'avons pas appelé notre propre méthode FindClass pour essayer le chargement. Ceci est le modèle de base (supprimer la violation de l'image de vol):
Il est également très simple à mettre en œuvre. La clé est la méthode LoadClass de la classe Classloader. Le code source est le suivant:
Class protégé <?> LoadClass (String Name, Boolean Resolve) lève ClassNotFoundException {Synchronized (getClassloadingLock (name)) {// Tout d'abord, vérifiez si la classe a déjà été la classe chargée <?> C = findloadClass (name); if (c == null) {long t0 = System.NanoTime (); essayez {if (parent! = null) {c = parent.loadClass (name, false); } else {c = findbootstrapClassorNull (name); }} catch (classNotFoundException e) {// classNotFoundException lancé si la classe n'est pas trouvée // à partir du chargeur de classe parent non nulle} if (c == null) {// Si non trouvé, alors invoquez laclasse dans l'ordre // pour trouver la classe. long t1 = System.NanoTime (); c = findClass (name); // c'est le chargeur de classe déterminant; Enregistrez les statistiques Sun.Misc.PerfCounter.GetParentDelegationTime (). AddTime (T1 - T0); Sun.Misc.PerfCounter.getFindCLasStime (). AddElapSedTimeFrom (T1); Sun.Misc.perfcounter.getFindClasses (). Incmenment (); }} if (résolve) {class (c); } return c; }}Soudain, je me suis senti taquiné, pourquoi ai-je juste lancé l'exception directement? En fait, c'est parce que la classe Classloader est une classe abstraite. En fait, il écrira une sous-classe lors de l'utilisation. Cette méthode sera réécrite au besoin pour terminer le processus de chargement requis par l'entreprise.
Classloader personnalisé
Lors de la personnalisation de la sous-classe de Classloader, il existe deux méthodes courantes: l'une consiste à réécrire la méthode LoadClass, et l'autre consiste à réécrire la méthode FindClass. En fait, ces deux méthodes sont essentiellement les mêmes. Après tout, LoadClass appellera également FindClass, mais logiquement parlant, il est préférable de ne pas modifier directement la logique interne de LoadClass.
Je pense personnellement que la meilleure façon est de simplement réécrire la méthode de chargement des classes personnalisées dans FindClass.
Pourquoi est-ce mieux? Parce que j'ai également dit plus tôt que la méthode LoadClass est l'endroit idéal pour implémenter la logique du modèle délégué par les parents. La modification de cette méthode sans autorisation entraînera la détruire du modèle et causera facilement des problèmes. Par conséquent, il est préférable d'apporter des changements à petite échelle dans le cadre du modèle de délégation parent pour ne pas détruire la structure stable d'origine. Dans le même temps, il évite également la nécessité d'écrire du code en double délégué par les parents dans le processus de réécriture de la méthode LoadClass. Du point de vue de la réutilisabilité du code, ne pas modifier directement cette méthode est toujours un meilleur choix.
Bien sûr, ce serait différent si vous détruisez délibérément le modèle de commission des parents.
Détruisez le modèle de délégation parentale
Pourquoi détruire le modèle de délégation des parents?
En fait, dans certains cas, nous devrons peut-être charger deux classes différentes, mais malheureusement, les noms de ces deux classes sont exactement les mêmes. Pour le moment, le modèle de délégation des parents ne peut pas répondre à nos exigences. Nous devons réécrire la méthode LoadClass pour détruire le modèle de délégation parent et laisser le même nom de classe se charger plusieurs fois. Bien sûr, la destruction mentionnée ici n'est que la destruction au sens local.
Mais les noms de classe sont les mêmes, comment JVM peut-il distinguer ces deux classes? De toute évidence, cela ne provoquera aucun effondrement de la vision du monde. En fait, les classes sont non seulement limitées par les noms de classe dans JVM, mais appartiennent également au Classloader qui les charge. Les classes chargées de différents chargeurs de classe ne se affectent pas en fait.
Faire une expérience.
Écrivons d'abord deux classes:
package com.mythsman.test; classe publique Bonjour {public void Say () {System.out.println ("Ceci est de Hello V1"); }} package com.mythsman.test; classe publique Bonjour {public void Say () {System.out.println ("Ceci est de Hello V2"); }} Les deux noms de classe sont les mêmes, la seule différence est que la mise en œuvre des méthodes est différente. Nous compilons d'abord séparément, puis renomons les fichiers de classe générés sur hello.class.1 et bonjour.class.2.
Notre objectif est de créer des instances de ces deux classes dans la classe de test.
Ensuite, nous créons une nouvelle classe de test com.mythsman.test.main et créons deux chargeurs de classe personnalisés dans la fonction principale:
Classloader classloader1 = new classloader () {@Override public class <?> LoadClass (String S) lève ClassNotFoundException {try {if (s.equals ("com.mythsman.test.hello")) {byte [] classbytes = files.readallbytes (paths.get ("/ home / myths / bushtop / Testing / Hello.1"); return Deficlass (s, classBytes, 0, classBytes.Length); } else {return super.loadClass (s); }} catch (ioException e) {lancez new classNotFoundException (s); }}}; Classloadher classloadher2 = new classloadher () {@Override public class <?> LoadClass (String S) lève ClassNotFoundException {try {if (s.equals ("com.mythsman.test.hello")) {byte [] classBytes = Files.ReadallBytes (paths.get ("/ home / mythes / Desktop / test / hello.class.2")); return Deficlass (s, classBytes, 0, classBytes.Length); } else {return super.loadClass (s); }} catch (ioException e) {lancez new classNotFoundException (s); }}};
Le but de ces deux chargeurs de classe est d'associer deux codes bytecodes différents de la classe Hello séparément. Nous devons lire le fichier bytecode et le charger en classe via la méthode de définition. Notez que nous surchargeons la méthode LoadClass. Si nous surchargez la méthode FindClass, alors en raison du mécanisme de traitement du délégué parent de la méthode LoadClass, la méthode FindClass du deuxième chargeur de classe ne sera pas appelée.
Alors, comment générer des instances? De toute évidence, nous ne pouvons pas référer directement aux noms de classe (conflits de nom), nous ne pouvons donc utiliser que la réflexion:
Objet hellov1 = classloadher1.loadclass ("com.mythsman.test.hello"). NewInstance (); objet; hellov2 = classloadher2.loadclass ("com.mythsman.test.hello"). newInstance (); hellov1.getClass (). getMethod ("Say"). invoke (hellov1); hellov2.getclass (). getMethod ("Say"). invoke (hellov2); Sortir:
Ceci est de Hello v1s issu de Hello v2
Ok, même si vous avez terminé deux charges, il reste encore quelques points à faire attention.
Quelle est la relation entre deux classes
De toute évidence, ces deux classes ne sont pas la même classe, mais leurs noms sont les mêmes. Alors, quels sont les résultats des opérateurs comme Isinstance de:
System.out.println ("class:" + hellov1.getclass ()); System.out.println ("class:" + hellov20class ()); System.out.println ("a hcode: "+ hellov2.getClass (). hashcode ()); System.out.println (" classloader: "+ hellov1.getclass (). getClassLoader ()); System.out.println (" classloader: "+ hellov2.getClass (). getClassloader ()); Sortir:
Classe: Classe com.mythsman.test.helloclass: classe com.mythsman.test.hellohashcode: 1581781576hashcode: 1725154839classloader: com.mythsman.test.main$1@5e2De80cclassloader: com.mythsman.test.mainagne
Leurs noms de classe sont en effet les mêmes, mais les codes de hashs de la classe sont différents, ce qui signifie que ces deux ne sont essentiellement pas la même classe, et leurs chargeurs de classe sont également différents (en fait, ce sont les deux classes internes de Main).
Quelle est la relation entre ces deux chargeurs de classe et le chargeur de classe à trois couches du système?
Prenez le premier chargeur de classe personnalisé à titre d'exemple:
System.out.println (classloadher1.getParent (). GetParent (). GetParent ()); System.out.println (classloader1.getParent (). GetParent ()); System.out.println (Classlo ader1.getParent ()); System.out.println (classloadher1.getParent ()); System.out.println (classloader1); System.out.println (classloader.getSystemClassloader ());
Sortir:
nullsun.menc.launcher $ extclassloadher @ 60e53b93sun.menc.launcher $ appclassloader @ 18b4aac2com.mythsman.test.main $ 1 @ 5e2de80csun.misc.launcher $ appclassloader @ 18b4aac2
Bien sûr, la relation parent-son mentionnée ici n'est pas une relation d'héritage, mais une relation combinée. L'enfant Classloader enregistre une référence (parent) du Parent Classloader.