Confundir
En el pasado, al mirar el código fuente, siempre encontré el código en el marco usando Thread.CurrentThread.getContextClassLoader () para obtener el cargador de clase de contexto del subproceso actual, y use este cargador de clase de contexto para cargar la clase.
Cuando generalmente escribimos código en un programa, cuando queremos cargar clases dinámicamente, generalmente usamos class.forname () para cargar las clases que necesitamos. Por ejemplo, lo más común es que cuando estamos programando JDBC, usamos class.forname () para cargar el controlador JDBC.
Pruebe {return class.forname ("oracle.jdbc.driver.oracledriver");} catch (classNotFoundException e) {//}Entonces, ¿por qué cuando usamos class.forname () para cargar una clase, si no se puede encontrar la clase, aún intentamos cargar la clase usando el cargador de clase obtenido por Thread.CurrentThread.getContextLoader ()? Por ejemplo, podemos encontrar el siguiente código:
Pruebe {return class.forname (className);} Catch (ClassNotFoundException e) {// skip} classloader ctxclassLoader = thread.currentThread (). getContextClassLoader (); if (ctxclassloader! = null) {try {clazz = ctxclassloader.loadclass (className); } Catch (ClassNotFoundException e) {// Skip}}La parte en negrita aquí es usar el cargador obtenido por Thread.CurrentThread.getContextLoader () para cargar la clase. Obviamente, el cargador de clase utilizado cuando class.forname () Carga la clase puede ser diferente del cargador de clase obtenido por hilt.currentThread.getContextLoader (). Entonces, ¿por qué ocurre la diferencia?
Cargador de clase Java
Antes de comprender por qué se utiliza este cargador de clase obtenido por Thread.CurrentThread.getContextLoader (), primero comprendamos el cargador de clases (ClassLoader) utilizado en el JVM.
Hay tres tipos de cargadores de clases en JVM de forma predeterminada:
Cargador de clase de arranque
El cargador de clases de la clase Bootstrap es un cargador de clase integrado en JDK, que se utiliza para cargar clases dentro de JDK. El cargador de clase Bootstrap se usa para cargar las clases debajo de $ java_home/jre/lib/lib en el JDK, como las clases en el paquete RT.jar. El cargador de clase Bootstrap es parte del JVM y generalmente se escribe en código nativo.
Cargador de clase de extensión
El cargador de clase de cargador de clase de extensión se utiliza principalmente para cargar clases en el paquete de extensión JDK. En general, los paquetes bajo $ java_home/lib/ext se cargan a través de este cargador de clase, y las clases bajo este paquete básicamente comienzan con Javax.
Cargador de clase del sistema
System Class Loader Class Loader también se llama Aplicación Class Loader (AppClassLoader). Como su nombre indica, este cargador de clase se usa para cargar el código de aplicación que los desarrolladores suelen escribir. El cargador de clase del sistema se utiliza para cargar clases de nivel de aplicación almacenadas en la ruta ClassPath.
El siguiente código enumera estos tres cargadores de clase:
public class 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 ()); }}Dónde obtener Bootstrap Class Loader Devuelve un valor nulo para siempre
NULL # Bootstrap Class Loader Sun.misc.launcher$extclassloader@5e2de80c # Extension Class Loader Sun.Misc.Launcher$appclassLoader@18B4AAC2 # System Class Loader
Modelo de delegación matriz
Los tres cargadores de tipo introducidos anteriormente no están aislados, tienen una relación jerárquica:
Los tres cargadores de clase trabajan juntos a través de esta relación jerárquica y son responsables de la carga de clases juntos. El modelo jerárquico anterior se llama modelo de "Delegación matriz" del cargador de clase. El modelo de delegación principal requiere que todos los cargadores de clase tengan un cargador principal, excepto el cargador de clase de arranque de nivel superior. Cuando el cargador de clase carga una clase, primero verifique si hay clases en el caché que se han cargado. Si no, entonces el cargador principal se delega para cargar la clase primero. El cargador principal realiza el mismo trabajo que el cargador infantil anterior hasta que la solicitud alcanza el cargador de clase Bootstrap de nivel superior. Si el cargador principal no puede cargar la clase requerida, entonces el cargador infantil intentará cargar la clase por sí solo. Funciona de manera similar a la siguiente:
Podemos usar el código en ClassLoader en JDK para ver la implementación del mecanismo de delegación matriz. El código se implementa en classloader.loadClass ()
Clase rotada <?> LoadClass (nombre de cadena, Boolean Resolve) lanza ClassNotFoundException Synchronized (getClassLoLoadingLock (name)) {// Primero, verifique si la clase ya se ha cargado de clase <?> c = findloadEdClass (name); if (c == null) {long t0 = system.nanotime (); intente {if (parent! = null) {c = parent.loadclass (nombre, falso); } else {c = findBootstrapClassOnnull (nombre); }} Catch (ClassNotFoundException e) {// classnotFoundException lanzado si la clase no se encuentra // desde el cargador de clase principal no nulo} if (c == null) {// Si aún no se encuentra, luego invoque FindClass en orden // para encontrar la clase. largo T1 = System.nanotime (); c = FindClass (nombre); // Este es el cargador de clase definitorio; Registre las estadísticas sun.misc.perfcounter.getParentDelegationTime (). Addtime (t1 - t0); Sun.misc.perfcounter.getFindClasStime (). AddelapsedTimeFrom (T1); sun.misc.perfcounter.getFindClasses (). increment (); }} if (resolve) {resolveclass (c); } return c; }Una ventaja de usar la delegación matriz para organizar cargadores de clase es estar seguro. Si definimos una clase de cadena nosotros mismos, esperamos reemplazar esta clase de cadena con la implementación de java.lang.string en el Java predeterminado.
Ponemos el archivo de clase de la clase de cadena que implementamos en la ruta ClassPath. Cuando usamos el cargador de clase para cargar la clase de cadena que implementamos, primero, el cargador de clases delegará la solicitud al cargador principal, y a través de la capa por capa, el cargador de clase Bootstrap finalmente cargará el tipo de cadena en el paquete RT.jar, y luego nos lo devolverá todo el camino. En este proceso, nuestro cargador de clase ignora la clase de cadena que colocamos en el classpath.
Si no se adopta el mecanismo de delegación matriz, el cargador de clase del sistema puede encontrar el archivo de clase de cadena en la ruta ClassPath y cargarlo en el programa, lo que hace que la implementación de la cadena en el JDK esté sobrescribida. Por lo tanto, este método de trabajo de los cargadores de clase asegura que los programas Java puedan ejecutarse de manera segura y estable hasta cierto punto.
Cargador de clase de contexto de hilo
Lo anterior habla sobre tantos contenido relacionado con los cargadores de clase, pero aún no habla sobre el tema actual, los cargadores de clases de contexto de hilo.
En este punto, ya sabemos que Java proporciona tres cargadores de tipo y trabaja en concierto de acuerdo con un mecanismo de delegación de padres estrictos. En la superficie, parece perfecto, pero es este estricto mecanismo de delegación matriz lo que causa algunas limitaciones al cargar clases.
Cuando nuestro marco más básico necesita usar clases de nivel de aplicación, solo podemos usar estas clases si esta clase se carga cuando se puede cargar el cargador de clase utilizado por nuestro marco actual. En otras palabras, no podemos usar el cargador de clase del cargador de clase actual. Esta limitación es causada por el mecanismo de delegación matriz, porque la delegación de las solicitudes de carga de clase es unidireccional.
Aunque no hay muchos casos, todavía hay tal demanda. Un servicio JNDI típico. JNDI proporciona una interfaz para consultar recursos, pero la implementación específica es implementada por diferentes fabricantes. En este momento, el código de JNDI está cargado por el cargador de clase Bootstrap de JVM, pero la implementación específica es un código que no sea el JDK proporcionado por el usuario, por lo que solo puede ser cargado por el cargador de clase del sistema u otro cargador de clase definido por el usuario. Según el mecanismo de delegación matriz, JNDI no puede obtener la implementación del SPI de JNDI.
Para resolver este problema, se introduce un cargador de clase de contexto de hilo. SetContextClassLoader () del Java.lang.Thread Class SetContextClassLoader () (si no está configurado, se heredará desde el hilo principal de forma predeterminada. Si el programa no lo ha establecido, se predeterminará al cargador de clase del sistema). Con el cargador de clase de contexto de hilo, la aplicación puede pasar el cargador de clase utilizada por la aplicación al código que utiliza el cargador de clase de nivel superior a través de java.lang.thread.setContextClassLoader (). Por ejemplo, el servicio JNDI anterior puede usar este método para obtener un cargador de clase que pueda cargar implementaciones SPI y obtener las clases de implementación SPI requeridas.
Se puede ver que la introducción de cargadores de clase roscados es en realidad una destrucción del mecanismo de delegación matriz, pero proporciona flexibilidad en la carga de clase.
Resolver dudas
Volviendo al principio, para cargar clases implementadas por usuarios fuera del marco, estas clases no pueden cargarse a través del cargador de clases utilizado por el marco. Para omitir el modelo de delegación principal del cargador de clase, se utiliza thread.getContextClassLoader () para cargar estas clases.
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.