Embrouiller
Dans le passé, lorsque je regarde le code source, j'ai toujours rencontré le code dans le framework à l'aide de thread.currentThread.getContextClassLoader () pour obtenir le chargeur de classe de contexte du thread actuel, et utilisez ce chargeur de classe de contexte pour charger la classe.
Lorsque nous écrivons généralement du code dans un programme, lorsque nous voulons charger des classes dynamiquement, nous utilisons généralement class.forname () pour charger les classes dont nous avons besoin. Par exemple, la chose la plus courante est que lorsque nous programmons JDBC, nous utilisons class.forname () pour charger le pilote JDBC.
try {return class.forname ("oracle.jdbc.driver.oracledriver");} catch (classNotFoundException e) {// skip}Alors pourquoi lorsque nous utilisons class.Forname () pour charger une classe, si la classe ne peut pas être trouvée, nous essayons toujours de charger la classe en utilisant le chargeur de classe obtenu par thread.currentThread.getContextLoader ()? Par exemple, nous pouvons rencontrer le code suivant:
try {return class.forname (className);} catch (classNotFoundException e) {// skip} classloadher ctxclassloader = thread.currentThread (). getContextClassLoader (); if (ctxclassloadher! = null) {try {Clazz = ctxclassloader.loadClass (className); } catch (classNotFoundException e) {// skip}}La partie en gras ici est d'utiliser le chargeur obtenu par thread.currentThread.getContextLoader () pour charger la classe. De toute évidence, le chargeur de classe utilisé lorsque class.forname () charge la classe peut être différente du chargeur de classe obtenu par thread.currentThread.getContextLoader (). Alors pourquoi la différence se produit-elle?
Chargeur de classe Java
Avant de comprendre pourquoi ce chargeur de classe obtenu par thread.currentThread.getContextLoader () est utilisé, comprenons d'abord le chargeur de classe (classloader) utilisé dans le JVM.
Il existe trois types de chargeurs de classe dans JVM par défaut:
Chargeur de classe bootstrap
Le chargeur de classe Bootstrap Class Chardeur est un chargeur de classe intégré à JDK, qui est utilisé pour charger des classes à l'intérieur de JDK. Le chargeur de classe bootstrap est utilisé pour charger les classes en dessous de $ java_home / jre / lib dans le JDK, comme les classes du package RT.Jar. Le chargeur de classe bootstrap fait partie du JVM et est généralement écrit en code natif.
Chargeur de classe d'extension
Le chargeur de classe de chargeur de classe d'extension est principalement utilisé pour charger des classes dans le package d'extension JDK. Généralement, les packages sous $ java_home / lib / ext sont chargés via ce chargeur de classe, et les classes de ce package commencent essentiellement avec Javax.
Chargeur de classe système
Le chargeur de classe du chargeur de classe système est également appelé chargeur de classe d'application (AppClassload). Comme son nom l'indique, ce chargeur de classe est utilisé pour charger le code d'application que les développeurs écrivent généralement. Le chargeur de classe système est utilisé pour charger des classes au niveau de l'application stockées dans le chemin de classe de classe.
Le code suivant répertorie ces trois chargeurs de classe:
classe publique mainclass {public static void main (String [] args) {System.out.println (Integer.class.getClassloader ()); System.out.println (logging.class.getClassloader ()); System.out.println (Mainclass.class.getClassloader ()); }}Où obtenir le chargeur de classe bootstrap renvoie une valeur nulle pour toujours
NULL # Bootstrap Class Loder Sun.Misc.Launcher$ExtClassLoader@5E2DE80C # Extension Classe de charge Sun.Misc.Launcher$AppCLASSOLODER@18B4AAC2 # Classeur Système Chardeur de classe System
Modèle de délégation parentale
Les trois chargeurs de type introduits ci-dessus ne sont pas isolés, ils ont une relation hiérarchique:
Les trois chargeurs de classe travaillent ensemble grâce à cette relation hiérarchique et sont responsables du chargement des classes ensemble. Le modèle hiérarchique ci-dessus est appelé modèle "Delégation parent" du chargeur de classe. Le modèle de délégation parent nécessite que tous les chargeurs de classe doivent avoir un chargeur parent à l'exception du chargeur de classe bootstrap de niveau supérieur. Lorsque le chargeur de classe charge une classe, vérifiez d'abord s'il y a des classes dans le cache qui ont été chargées. Sinon, le chargeur parent est délégué pour charger d'abord la classe. Le chargeur parent effectue le même travail que le chargeur enfant précédent jusqu'à ce que la demande atteigne le chargeur de classe bootstrap de niveau supérieur. Si le chargeur parent ne peut pas charger la classe requise, le chargeur enfant essaiera de charger la classe par lui-même. Cela fonctionne comme ce qui suit:
Nous pouvons utiliser le code dans Classloader dans JDK pour voir la mise en œuvre du mécanisme de délégation parent. Le code est implémenté dans classloader.loadClass ()
Class rotated <?> 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) {résolveclass (c); } return c; }Un avantage de l'utilisation de la délégation parent pour organiser les chargeurs de classe est d'être sûr. Si nous définissons nous-mêmes une classe de chaînes, nous espérons remplacer cette classe de chaîne par l'implémentation de java.lang.string dans le Java par défaut.
Nous mettons le fichier de classe de la classe String que nous avons implémentée dans le chemin de classe de classe. Lorsque nous utilisons le chargeur de classe pour charger la classe de chaîne que nous avons implémentée, d'abord, le chargeur de classe déléguera la demande au chargeur parent, et via la couche par couche, le chargeur de classe bootstrap chargera enfin le type de chaîne dans le package RT.JAR, puis le renvoyez sur nous. Dans ce processus, notre chargeur de classe ignore la classe de chaîne que nous avons placée dans le chemin de classe.
Si le mécanisme de délégation parent n'est pas adopté, le chargeur de classe système peut trouver le fichier de classe de chaîne dans le chemin de classe de classe et le charger dans le programme, ce qui a permis à la mise en œuvre de la chaîne dans le JDK. Par conséquent, cette méthode de fonctionnement de chargeurs de classe garantit que les programmes Java peuvent s'exécuter en toute sécurité et stable dans une certaine mesure.
Chargeur de classe de contexte de thread
Ce qui précède parle de tant de contenus liés aux chargeurs de classe, mais ne parle toujours pas du sujet d'aujourd'hui, des chargeurs de classe de contexte de thread.
À ce stade, nous savons déjà que Java fournit trois chargeurs de type et fonctionne de concert selon un mécanisme de délégation parent strict. En surface, il semble parfait, mais c'est ce mécanisme de délégation parent strict qui provoque certaines limites lors du chargement des classes.
Lorsque notre cadre plus basique doit utiliser des classes au niveau de l'application, nous ne pouvons utiliser ces classes que si cette classe est chargée lorsque le chargeur de classe utilisé par notre cadre actuel peut être chargé. En d'autres termes, nous ne pouvons pas utiliser le chargeur de classe du chargeur de classe actuel. Cette limitation est causée par le mécanisme de délégation des parents, car la délégation des demandes de chargement de classe est à sens unique.
Bien qu'il n'y ait pas beaucoup de cas, il y a encore une telle demande. Un service JNDI typique. JNDI fournit une interface aux ressources de requête, mais l'implémentation spécifique est implémentée par différents fabricants. À l'heure actuelle, le code de JNDI est chargé par le chargeur de classe bootstrap de JVM, mais l'implémentation spécifique est un code autre que le JDK fourni par l'utilisateur, de sorte qu'il ne peut être chargé que par le chargeur de classe système ou un autre chargeur de classe défini par l'utilisateur. Dans le cadre du mécanisme de délégation parent, JNDI ne peut pas obtenir la mise en œuvre de SPI de JNDI.
Pour résoudre ce problème, un chargeur de classe de contexte de thread est introduit. SetContextClassLoader () de la classe java.lang.thread setContextClassLoader () (s'il n'est pas défini, il sera hérité du thread parent par défaut. Si le programme ne l'a pas défini, il sera par défaut au chargeur de classe système). Avec le chargeur de classe de contexte de thread, l'application peut transmettre le chargeur de classe utilisé par l'application au code qui utilise le chargeur de classe de niveau supérieur via java.lang.thread.setContextClassLoader (). Par exemple, le service JNDI ci-dessus peut utiliser cette méthode pour obtenir un chargeur de classe qui peut charger des implémentations SPI et obtenir les classes d'implémentation SPI requises.
On peut voir que l'introduction des chargeurs de classe filetés est en fait une destruction du mécanisme de délégation parent, mais il offre une flexibilité dans le chargement des classes.
Résoudre les doutes
De retour au début, afin de charger des classes implémentées par les utilisateurs en dehors du cadre, ces classes peuvent ne pas être chargées via le chargeur de classe utilisé par le cadre. Afin de contourner le modèle de délégation parent du chargeur de classe, thread.getContextClassloader () est utilisé pour charger ces classes.
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.