Hay dos tipos de cargas de clases en Java, uno está definido por el usuario y el otro es el cargador de clase Bootstrap integrado en JVM.
Hay tres tipos de cargadores de clase integrados en JVM, a saber, Bootstrap ClassLoader, Extension ClassLoader (es decir, ExtClassLoader) y System ClassLoader (es decir, AppClassLoader).
No hablaré sobre la delegación matriz cuando se cargue JVM, hay muchos artículos sobre Javaeye que se introducen ...
Puede echar un vistazo a sus constructores por separado, donde el cargador de clases Bootstrap está escrito en c.
java.lang.classloader
ClassLoader Protected (ClassLoader Parent) verdadero; inicializado = verdadero;
Este constructor tiene dos parámetros y ningún constructor. El constructor con parámetros pasa en el cargador principal de este cargador de clase, mientras que el constructor sin parámetros tratará el cargador de clase devuelto por getSystemClassLoader () como su propio cargador principal.
Public ClassLoader getSystemClassLoader () {// El cargador de clase devuelto se asigna InitsystemClassLoader (); ccl = getCallerClassLoader (); if (! sclset) {if (scl! = null) tirar nueva ilegalstateException ("invocación recursiva"); oops = null; // El valor se asigna scl = l.getClassLoader (); .... .....................................}} sclset = true; El cargador de clase principal aquí es SCL, que se obtiene por L.GetClassLoader (), getClassLoader () y luego mire el código fuente del lanzador:
Private static Launcher Launcher = new Launcher ();
public staticuncher getLauncher () {return Launcher; ); AppClassLoader, es decir, el getSystemClassLoader devuelve AppClassLoader Loader = AppClassLoader.getAppClassLoader (ExtCl); Hilo, para evitar la confusión causada por las cargas de clases en Multithreads (entiendo esto yo mismo, jaja) // también establece el cargador de clases de contexto para el hilo principal. ...................................................... .................................................... .. ..............} / * * Devuelve el cargador de clase utilizado para iniciar la aplicación principal. A partir de esto, vemos que el cargador principal de AppClassLoader es ExtClassLoader y ¿cuál es el cargador principal de ExtClassLoader? Veamos el constructor de carneros extensas:
Public ExtClassLoader (Archivo [] Dirs) lanza IoException {super (getExturls (dirs), nulo, fábrica);Su cargador de padres está vacío, mientras que su clase principal de nivel superior es Java.lang.ClassLoader.
Clase sincronizada protegida <?> LoadClass (nombre de cadena, resolución booleana) arroja classNotFoundException {// Primero, verifique si la clase ya ha sido cargada C = FindLoadEdClass (name); ! = null) {// Llame al cargador principal para cargar. ) {// si aún no se encuentra, entonces invoca FindClass en orden // para encontrar la clase.Aquí, FindBootstraPClass0 es llamar al cargador de clases Bootstrap, el cargador de clase más central, para cargar la clase.
Finalmente, podemos ver que el cargador de clase devuelto por GetSystemClassLoader () es AppClassLoader.
Análisis de mecanismo de cargador de clases de Java
JDK Classloader predeterminado
JDK proporciona los siguientes cargadores de clase de forma predeterminada
Cargador bootstrp
El cargador BootStrp está escrito en el lenguaje C ++. JRE/clases.
Carro de extinción
El cargador BootStrp carga el carnero exttlass y establece el cargador principal del cardador extclass en el cargador bootstrp. Esto se clasifica en todos los directorios de la ruta y las 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. Es un documento JAR, que también es el cargador de clase predeterminado para programas Java.
En resumen, la relación entre ellos se puede describir en la siguiente figura:
Modelo de delegación matriz
La carga de la carga de clases en Java adopta un mecanismo de delegado principal.
Actualmente, el cargador de clases primero verifica si esta clase se ha cargado desde la clase que ya se ha cargado.
Cada cargador de clase tiene su propio caché de carga.
Cuando no se encuentra la memoria caché de la clase, el cargador de clase principal se delega para cargarlo. Manera de 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, las capacidades interactivas de diferentes cargadores de clases se incrementan. Matda su programa Hay tantos cargadores de clases, por lo que estas clases realmente se pueden compartir, lo que evita la confusión causada por diferentes cargadores de clase después de cargar diferentes clases del mismo nombre.
Cómo personalizar el cargador de clases
Además del carga de clase predeterminada mencionada anteriormente, Java también permite que las aplicaciones personalicen la carga de clases. Necesitamos prestar atención a varios métodos importantes:
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. 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 (nombre de cadena, resolución booleana) lanza ClassNotFoundException {// Primero, verifique si la clase ya se ha cargado Clase C = FindLoadEdClass (nombre); // Verifique si la clase se ha cargado si (c = C = C = = null) {try {if (parent! = null) {c = parent.loadclass (nombre, falso); } else {c = findBootstrapClass0 (nombre); // Si no hay cargador de clase principal, delegar el cargador de bootstrap para cargar}} capt (classnotFoundException e) {// si aún no se encuentra, luego invoque e findclass en orden // a Encuentra la clase. }} 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.
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. 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);}Desde el código anterior, podemos ver que este método se define como final, lo que significa que este método no puede anularse. El archivo debe cumplir con la definición de clases especificadas por la especificación de 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 porque es para la clase que comienza con Java.*,,, JVM La implementación ha asegurado que Bootstrp debe cargarla.
Escenarios que no siguen el "mecanismo de delegación de padres"
La mencionada anteriormente que el mecanismo de la delegación principal es principalmente para realizar el problema de interacción de las clases cargadas entre diferentes cargadores de clases. Cargador de clase principal en Java. Hablemos de la ocurrencia de esta situación.
Hay un estándar SPI (interfaz de proveedor de servicios) en Java, que utiliza bibliotecas SPI, como JDBC, JNDI, etc. Todos sabemos que JDBC necesita controladores proporcionados por terceros, y el paquete de jarras del controlador se coloca en nuestra aplicación misma. ClassPath, y la API de JDBC es parte de la provisión de JDK, y BootstrP lo ha cargado. Java presenta el concepto de carga de la clase de hilo. Conductor, está bien cargando a través del cargador de clase de contexto del hilo.
Además, para implementar un cargador de clase más flexible OSGI y algunos servidores de aplicaciones Java, también rompe el mecanismo de delegación matriz.