Al hacer el desarrollo de Java, el conocimiento básico con el que debe estar familiarizado en el mecanismo de cargadores de clases. Este artículo resume brevemente el mecanismo Java ClassLoader. Debido a que diferentes implementaciones de JVM son diferentes, el contenido descrito en este artículo está limitado a Hotspot JVM.
Este artículo comenzará con los cuatro aspectos del modelo de delegación matriz proporcionado por JDK, cómo personalizar el cargador de clases y los escenarios que rompan el mecanismo de delegación matriz en Java.
JDK Classloader predeterminado
JDK proporciona los siguientes cargadores de clase de forma predeterminada
Cargador bootstrp
El cargador BootStrp está escrito en lenguaje C ++. Se inicializa después de que se inicia la máquina virtual Java. Es principalmente responsable de cargar la ruta especificada por el parámetro %java_home %/jre/lib, -xbootclasspath y las clases en %java_home %/jre/classes.
Carro de extinción
El cargador BootStrp carga el carnador ExtClassScass y establece el cargador principal del cardador extclasss a bootstrp loader.extClassLoader está escrito en Java, específicamente, sun.misc.launcher $ extclassLoader. El Carga de ClassSass carga principalmente %Java_Home %/JRE/Lib/Ext, todos los directorios de clases en esta ruta y bibliotecas de clase en la ruta especificada por la variable del sistema Java.Ext.DIRS.
Cotizador de aplicaciones
Después de que el cargador de BootStrp cargue el cardador ExtClassShass, se cargará el carterista AppClass y el cargador principal del AppClassLoader se especifica como ExtClassLoader. AppClassLoader también está escrito en Java. Su clase de implementación es Sun.Misc.Launcher $ AppClassLoader. Además, sabemos que hay un método GetSystemClassLoader en ClassLoader. Este método devuelve el AppClassLoader.AppClassLoader es el principal responsable de cargar el documento de clase o JAR en la ubicación especificada por classpath. También es el cargador de clase predeterminado para programas Java.
Modelo de delegación matriz
La carga de cargador de clases en Java adopta un mecanismo de delegado matriz. Al cargar clases que usan un mecanismo de delegado matriz, se adoptan los siguientes pasos:
Actualmente, el cargador de clases primero verifica si esta clase se ha cargado desde la clase que ya se ha cargado. Si se ha cargado, devolverá directamente la clase original.
Cada cargador de clase tiene su propio caché de carga. Cuando se carga una clase, se colocará en el caché y se puede devolver directamente cuando se cargue la próxima vez.
Cuando no se encuentra el caché del cargador de clases, el cargador de clase principal se delega para cargar. El cargador de clase principal adopta la misma estrategia. Primero, verifique su propio caché y luego delegue la clase principal de la clase principal para que se cargue, hasta Bootstrp ClassLoader.
Cuando todos los cargadores de clase padre no están cargados, el cargador de clase actual los cargan y los coloca en su propio caché para que puedan devolverse directamente la próxima vez que haya una solicitud de carga.
Hablando de esto, puede preguntarse, ¿por qué Java adopta tal mecanismo de delegación? Para comprender este problema, presentamos otro concepto "espacio de nombres" sobre el cargador de clases, lo que significa que para determinar una determinada clase, necesita un nombre completamente calificado de la clase y cargamos este cargador de clase de clase para determinarlo conjuntamente. Es decir, incluso si los nombres completamente calificados de las dos clases son los mismos, porque diferentes cargadores de clases cargan esta clase, entonces es una clase diferente en el JVM. Después de comprender el espacio de nombres, echemos un vistazo al modelo delegado. Después de adoptar el modelo delegado, se incrementan las capacidades interactivas de diferentes cargadores de clases. Por ejemplo, como se mencionó anteriormente, las bibliotecas de clases proporcionadas por nuestro JDK Binsheng, como Hashmap, LinkedList, etc. Después de que estas clases se cargan por el cargador de clase BootstrP, sin importar cuántos cargadores de clases hay en su programa, estas clases realmente pueden compartirse, lo que evita la confusión causada por diferentes cargadores de clases que cargan diferentes clases del mismo nombre.
Cómo personalizar el cargador de clases
Además del cargador de clases proporcionado de forma predeterminada mencionada anteriormente, Java también permite que las aplicaciones personalicen el cargador de clases. Si desea personalizar el cargador de clases, necesitamos implementarlo heredando java.lang.classloader. A continuación, echemos un vistazo a varios métodos importantes a los que debemos prestar atención al personalizar el cargador de clases:
1.LoadClass Method
Método de carga de carga declarar
clase pública <?> LoadClass (nombre de cadena) lanza ClassNotFoundException
Lo anterior es la declaración prototipo del método LoadClass. La implementación del mecanismo de delegación matriz mencionado anteriormente se implementa realmente en este método. Echemos un vistazo al código de este método para ver cómo implementa la delegación principal.
Implementar el método LoadClass
public class <?> LoadClass (nombre de cadena) lanza ClassNotFoundException {return LoadClass (name, false);}De lo anterior, podemos ver que el método LoadClass llama al método LoadCclass (nombre, falso), así que echemos un vistazo a la implementación de otro método LoadClass.
Clase LoadClass (nombre de cadena, resolución booleana)
Clase sincronizada protegida <?> LoadClass (name de cadena, Boolean Resolve) lanza ClassNotFoundException {// Primero, verifique si la clase ya se ha cargado Clase C = FindLoadEdClass (name); // Compruebe si la clase se ha cargado si (c == null) {intenta {si (el padre! = null) {c = parent.loadclass (name, falso); // Se especifica el cargador, el cargador principal se delega para cargar. } else {c = findBootstrapClass0 (nombre); // Si no hay cargador de clase principal, luego delega el cargador de bootstrap para cargar}} catch (classnotFoundException e) {// si aún no se encuentra, luego invoque findclass en orden // para encontrar la clase. c = findclass (nombre); // Si la carga de la clase principal no está cargada, se cargará a través de su propia FindClass. }} if (resolve) {resolveclass (c);} return c;}En el código anterior, agregué comentarios para ver claramente cómo funciona el mecanismo de delegación principal de LoadClass. Una cosa que debemos tener en cuenta aquí es que la clase pública <?> LoadClass (nombre de cadena) arroja classnotFoundException no está marcada como final, lo que significa que podemos anular este método, lo que significa que el mecanismo de delegación principal puede romperse. Además, notamos que hay un método FindClass arriba. A continuación, hablemos de si este método es malo.
2.Findlass
Verificamos el código fuente de Java.lang.ClassLoader y encontramos que la implementación de FindClass es la siguiente:
Clase protegida <?> FindClass (nombre de cadena) lanza ClassNotFoundException {Throw New ClassNotFoundException (name);}Podemos ver que la implementación predeterminada de este método es lanzar directamente las excepciones, pero de hecho, este método se deja a nuestra aplicación para anular. La implementación específica depende de su lógica de implementación. Puede leer desde el disco o obtener el flujo de bytes de los archivos de clase de la red. Después de obtener el binario de clase, puede entregarlo para definir una mayor carga. Describamos definitivamente de forma más tarde. Ok, a través del análisis anterior, podemos sacar las siguientes conclusiones:
Cuando escribimos nuestro propio cargador de clases, si queremos seguir el mecanismo de delegación matriz, solo necesitamos anular FindClass.
3. Defineclass
Primero veamos el código fuente de definido:
definido
Clase final protegida <?> Defineclass (nombre de cadena, byte [] b, int off, int len) arroja classFormaterror {return definiteclass (name, b, off, len, null);}En el código anterior, podemos ver que este método se define como final, lo que significa que este método no se puede anular. De hecho, esta es también la única entrada que nos dejó JVM. A través de esta entrada única, JVM asegura que el archivo de clase debe cumplir con la definición de la clase especificada en la especificación de la máquina virtual Java. Este método finalmente llamará al método nativo para implementar la carga de la clase real.
Ok, a través de la descripción anterior, pensemos en la siguiente pregunta:
Si escribimos una clase java.lang.string nosotros mismos, ¿podemos reemplazar la clase que llama a JDK?
La respuesta es no. No podemos lograrlo. ¿Por qué? Veo muchas explicaciones en línea de que el mecanismo de delegación matriz resuelve este problema, pero en realidad no es muy preciso. Debido a que el mecanismo de delegación matriz se puede romper, puede escribir un cargador de clases para cargar la clase java.lang.string que escribió, pero encontrará que no se cargará con éxito. Específicamente, para las clases que comienzan con Java.*, La implementación de JVM ha asegurado que Bootstrp debe cargarlas.
Escenarios que no siguen el "mecanismo de delegación de padres"
La mencionada anteriormente que el mecanismo de delegación matriz es principalmente para realizar el problema de interacción de las clases cargadas entre diferentes cargadores de clases. Las clases que comparten todos son entregadas al cargador principal para cargar, pero de hecho hay una situación en Java donde las clases cargadas por el cargador de clase principal deben usar clases cargadas por el cargador infantil. Hablemos de la ocurrencia de esta situación.
Hay un estándar SPI (ServiceProviderInterface) en Java que utiliza bibliotecas SPI, como JDBC, JNDI, etc. Todos sabemos que JDBC requiere controladores proporcionados por terceros, y el paquete JAR del controlador se coloca en el Clase de nuestra aplicación en sí. La API de JDBC en sí es parte del JDK proporcionado por JDK, y Bootstrp lo ha cargado. Entonces, ¿cómo cargo las clases de implementación proporcionadas por los fabricantes de terceros? Java presenta el concepto de carga de clase de contexto de hilo. El cargador de la clase de subprocesos heredará desde el hilo principal de forma predeterminada. Si no se especifica, el predeterminado es el cargador de clase del sistema (AppClassLoader). De esta manera, cuando se carga un controlador de terceros, se puede cargar a través del cargador de clase de contexto del hilo.
Además, para implementar OSGI de cargador de clase más flexible y algunos servidores de javaapps, también rompe el mecanismo de delegación matriz.
Resumir
Lo anterior es todo el contenido de este artículo sobre el análisis del código del uso y el uso del mecanismo de cargador de clases Java. Espero que sea útil para todos. Los amigos interesados pueden continuar referiéndose a otros temas relacionados en este sitio. Si hay alguna deficiencia, deje un mensaje para señalarlo. ¡Gracias amigos por su apoyo para este sitio!