Proceso de carga de clase
El trabajo principal de un cargador de clase es cargar archivos de clase en el JVM. Como se muestra en la figura a continuación, el proceso se divide en tres pasos:
1. Carga: Localice el archivo de clase para cargar y cargar su secuencia de byte en el JVM;
2. Enlace: asigne la estructura de memoria más básica para cargar para guardar su información, como propiedades, métodos y clases referenciadas. En esta etapa, la clase aún no está disponible;
(1) Verificación: Verifique la corriente de byte cargada, como el formato y la seguridad;
(2) Asignación de memoria: prepare el espacio de memoria para esta clase para representar sus propiedades, métodos y clases referenciadas;
(3) Análisis: Cargue otras clases a las referenciadas por esta clase, como la clase principal, las interfaces implementadas, etc.
3. Inicialización: asigne valores a variables de clase.
El nivel de cargador de clase
La línea punteada en la figura a continuación son varios cargadores de clase importantes proporcionados por JDK, y la descripción detallada es la siguiente:
(1) Cargador de clase Bootstrap: al iniciar una clase que contiene la función principal, cargue el paquete jar en el directorio java_home/lib/-xbootclasspath especificado;
(2) Cargador de clase de extensión: Cargue el paquete JAR en el directorio Java_Home/Lib/Ext o -djava.ext.dirs especificado.
(3) Cargador de clase del sistema: cargar classpath o -djava.class.path class o paquete jar en el directorio especificado.
Lo que necesitas saber es:
1. Además del cargador de clase Bootstrap, otros cargadores de clase son subclases de la clase Java.lang.ClassLoader;
2. Bootstrap Class Loader no se implementa en Java. Si no usa un cargador de clase personalizado, entonces java.lang.string.class.getclassloader () es nulo, y el cargador principal de cargador de clase de extensión también es nulo;
3. Varias formas de obtener cargadores de clase:
(1) Obtener un cargador de clase Bootstrap: lo que obtienes debe ser nulo al intentar obtener el cargador de clase Bootstrap. Puede verificarlo de la siguiente manera: use el método GetClassLoader de los objetos de clase en el paquete RT.JAR, como java.lang.string.class.getclassloader () para obtener u obtener el cargador de clases de extensión, y luego llamar al método getparent para obtener;
(2) Obtenga el cargador de clase de extensión: use el método GetClassLoader del objeto de clase en el paquete JAR en el directorio Java_HOME/lib/ext o primero obtenga el cargador de clase del sistema, y luego obténlo a través de su método GetParent;
(3) Obtenga el cargador de clase del sistema: llame al método getClassLoader del objeto de clase que contiene la función principal, o llame a Thread.CurrentThread ().
(4) Obtenga el cargador de clase definido por el usuario: llame al método getClassLoader del objeto de clase o llame a thread.currentThread (). GetContextClassLoader ();
Principios operativos del cargador de clase
1. Principio del agente
2. Principio de visibilidad
3. El principio de singularidad
4. Principio proxy El principio de proxy se refiere a un cargador de clase que solicita que su proxy del cargador principal se cargue al cargar una clase, y el cargador principal también solicitará que su proxy del cargador principal se cargue, como se muestra en la figura a continuación.
¿Por qué usar el modo proxy? En primer lugar, esto puede reducir la carga duplicada de una clase. (¿Hay alguna otra razón?)
Dónde malinterpretar:
En general, se cree que el orden proxy del cargador de clase es el padre primero, es decir:
1. Al cargar una clase, el cargador de clase primero verifica si ha cargado la clase. Si se ha cargado, volverá; De lo contrario, pídale al cargador principal que sea proxy;
2. El cargador principal repite el funcionamiento de 1 hasta que el cargador de clase Bootstrap;
3. Si el cargador de clase Bootstrap no carga la clase, intentará cargarla, y si es exitoso, volverá; Si falla, se lanza una ClassNotFoundException, será cargada por el cargador infantil;
4. El cargador de subclase captura la excepción e intenta cargarlo. Si tiene éxito, regresa. Si falla, arroja una ClassNotFoundException hasta que se inicia el cargador de subclase cargado.
Esta comprensión es correcta para cargadores como el cargador de clase Bootstrap, el cargador de clases de extensión y el cargador de clases del sistema, pero algunos cargadores personalizados no son el caso. Por ejemplo, algunos cargadores de clase implementados por IBM Web Sphere Portal Server son el último padre, que es el cargador infantil que intenta cargar primero. Si la carga falla, se le preguntará al cargador principal. La razón de esto es: si espera que una determinada versión de LOG4J sea utilizada por todas las aplicaciones, colóquela en la biblioteca WAS_HOME y se cargará cuando se inicie. Si una aplicación quiere usar otra versión de LOG4J, no se puede lograr si usa primero los padres porque la clase en LOG4J ya está cargada en el cargador principal. Sin embargo, si se usa el último padre, el cargador de clase responsable de cargar la aplicación priorizará la carga de otra versión de log4j.
Principio de visibilidad
La visibilidad de cada clase al cargador de clase es diferente, como se muestra en la figura a continuación.
Para extender el conocimiento, OSGI aprovecha esta característica. Cada paquete está cargado por un cargador de clase separado, por lo que cada cargador de clase puede cargar una versión de una clase determinada, por lo que todo el sistema puede usar múltiples versiones de una clase.
El principio de singularidad
Cada clase se carga como máximo una vez en un cargador.
Conocimiento extendido 1: Para ser precisos, el patrón Singleton se refiere a un objeto Singleton que solo una copia de una clase en un conjunto de cargadores de clase.
Conocimiento extendido 2: Una clase puede ser cargada por múltiples cargadores de clase. Cada objeto de clase está en su propio espacio de nombres. Al comparar el objeto o tipo de clase convertir la instancia, comparará su espacio de nombres respectivo al mismo tiempo, como:
La clase Klass está cargada por ClassLoadEra, suponiendo que el objeto de clase es Klassa; Está cargado por ClassLoaderB, suponiendo que el objeto de clase sea Klassb, entonces Klassa no es igual a Klassb. Al mismo tiempo, cuando la instancia de ClassA se emite a Klassb, se lanzará una excepción de ClassCastException.
¿Por qué necesitas un cargador de clase personalizado? El cargador de clase personalizado agrega mucha flexibilidad al idioma Java. Los usos principales son:
1. Las clases se pueden cargar desde múltiples lugares, como en la red, en la base de datos o incluso los archivos de origen compilados instantáneamente;
2. Después de la personalización, el cargador de clase puede cargar una versión determinada del archivo de clase en principio en tiempo de ejecución;
3. Después de la personalización, el cargador de clases puede descargar dinámicamente algunas clases;
4. Después de la personalización, el cargador de clase puede descifrar y descomprimir la clase antes de cargar la clase.
Carga implícita y explícita de clases
Carga implícita: cuando se hace referencia a una clase, herede o se instancia, se cargará implícitamente. Si la carga falla, se arroja NoclassDefFoundError.
Carga explícita: use el siguiente método, si la carga falla, se lanzará una ClassNotFoundException.
Cl.LoadClass (), Cl es una instancia del cargador de clase;
Class.forname (), se carga usando el cargador de clase de la clase actual.
Si la ejecución del bloque estático de la clase tiene las siguientes clases:
paquete cn.fengd; Public Class Dummy {static {System.out.println ("HI"); }} Crea otra clase de prueba:
paquete cn.fengd; public class ClassLoaderTest {public static void main (string [] args) lanza InstanciationException, excepción {try { / * * diferentes formas de carga. */ Clase C = classloaderTest.class.getClassLoader (). LoadClass ("cn.fengd.dummy"); } Catch (Exception e) {// TODO Auto Generado Bloque E.PrintStackTrace (); }}}¿Qué tan efectivo es después de correr?
¿Qué pasa si es reemplazado por class.forname ("cn.fengd.dummy"); o nuevo Dummy ()?
Hola será salida.
Preguntas frecuentes:
1. ¿El tipo especificado cargado por diferentes cargadores de clase sigue siendo el mismo tipo?
En Java, una clase usa su nombre de clase totalmente calificado como su identificador. El nombre de la clase de coincidencia exacta mencionado aquí incluye el nombre del paquete y el nombre de la clase. Sin embargo, en el JVM, una clase usa su nombre completo y una instancia del cargador de clase de carga como identificadores únicos. Las clases cargadas por diferentes cargadores de clase se colocarán en diferentes espacios de nombres. Podemos usar dos cargadores de Esto se puede hacer escribiendo dos cargadores de clase personalizados para cargar el mismo tipo personalizado y luego hacer un juicio; Al mismo tiempo, puede probar la carga de Java.* Tipo, y luego comparar y probar los resultados de la prueba.
2. Llame directamente al método class.forname (nombre de cadena) en el código. ¿Qué cargador de clase activará el comportamiento de carga de clase?
Class.forname (nombre de cadena) utilizará el cargador de clase que llama a la clase a cargar clase de forma predeterminada. Analicemos directamente el código JDK correspondiente:
//java.lang.class.java publicstatic class <?> forname (string className) lanza ClassNotFoundException {return forname0 (className, true, classLoader.getCallerClassLoader ());} // java.lang.classLoader.Java///// getCallerClassLoader () {// Obtenga el tipo de clase de llamada de llamada (Caller) Caller = Reflection.GetCallerClass (3); // Esto puede ser nulo si la VM lo solicita si (Caller == NULL) {returnnull; } // Llame al método local en java.lang.class para obtener el cargador de clases que carga la clase de llamada (llamada) return Caller.getClassLoader0 ();} // java.lang.class.java//native implementación de la máquina virtual para obtener el cargador de clase de la clase de clase actual GetClassLoader0 (); 3. Al escribir un cargador de clase personalizado, si el cargador principal no está configurado, ¿qué es el cargador principal?
Cuando no se especifica el cargador de clase principal, el cargador de clase del sistema se usa de forma predeterminada. Es posible que algunas personas no lo entiendan, ahora echemos un vistazo a la implementación del código correspondiente de JDK. Como todos sabemos, escribimos un cargador de clase personalizado directa o indirectamente heredamos de la clase abstractor Java.lang.ClassLoader. El constructor predeterminado correspondiente sin parámetros se implementa de la siguiente manera:
// Extracto de java.lang.classloader.javaprotected classloader () {SecurityManager Security = System.getSecurityManager (); if (Security! = Null) {Security.CheckCreateClassLoader (); } this.parent = getSystemClassLoader (); inicialized = true;}Echemos un vistazo a la implementación correspondiente del método GetSystemClassLoader ():
PrivateStaticsynChronizedVoid InitsystemClassLoader () {// ... sun.misc.launcher l = sun.misc.launcher.getlauncher (); scl = l.getClassLoader (); // ...}Podemos escribir un código de prueba simple para probarlo:
System.out.println (sun.misc.launcher.getlauncher (). GetClassLoader ());
La salida correspondiente de esta máquina es la siguiente:
sun.misc.launcher$appclassloader@197d257
Por lo tanto, ahora podemos creer que cuando el cargador de clase personalizado no especifica el cargador de clase principal, el cargador de clase principal predeterminado es el cargador de clase del sistema. Al mismo tiempo, podemos sacar las siguientes conclusiones:
El cargador de clase definido por el usuario instantáneo no especifica el cargador de clase principal, entonces las clases en los siguientes tres lugares también se pueden cargar:
(1) Clases bajo <Java_runtime_Home>/Lib
(2) clases en la ubicación especificada por la variable del sistema java.ext.dir
(3) Clases en la ruta de clase de proyecto actual o en la ubicación especificada por la variable del sistema java.class.path
4. Al escribir un cargador de clase personalizado, ¿qué sucederá si el cargador de clase principal se ve obligado a ser nulo? Si el cargador de clase personalizado no puede cargar la clase especificada, ¿definitivamente fallará?
La especificación JVM estipula que si el cargador de clase definido por el usuario obliga al cargador de clase principal a NULL, el cargador de clase de inicio se establecerá automáticamente en el cargador de clase principal del cargador de clase definido por el usuario actual (este problema se ha analizado antes). Al mismo tiempo, podemos sacar las siguientes conclusiones:
Si el cargador de clase definido por el usuario instantáneo no especifica el cargador de clase principal, también puede cargar la clase en <java_runtime_home>/lib, pero la clase en <java_runtime_home>/lib/ext directorio no se puede cargar en este momento.
Nota: Las conclusiones inferidas de las preguntas 3 y 4 se basan en el cargador de clase definido por el usuario que continúa la lógica de delegación predeterminada de java.lang.classloader.loadclass (...). Si el usuario cambia esta lógica de delegación predeterminada, la conclusión inferida anterior puede no ser válida. Vea la pregunta 5 para más detalles.
5. ¿Cuáles son los puntos generales de atención al escribir cargadores de clase personalizados?
(1) Generalmente, trate de no anular la lógica de delegación en el método LoadClass (...) existente. En general, se realiza en versiones antes de JDK 1.2, y resulta que es muy probable que hacerlo haga que el cargador de clase predeterminado del sistema no funcione correctamente. En la especificación JVM y la documentación JDK (versiones 1.2 o posteriores), no se recomienda que los usuarios sobrescriban el método LoadClass (...). Por el contrario, se recuerda claramente a los desarrolladores que sobrescriban la lógica FindClass (...) al desarrollar un cargador de clase personalizado. Tome un ejemplo para verificar el problema:
// LOADER CLASE CONSEJO DEL USER CLASE CLASECTERCLASSLOCER.JAVA (Overwrite LoadClass Logic) publicClassWrongClassLoaderExtends classLoader {public class <?> LoadClass (name de cadena) lanza ClassNotFoundException {returnThis.FindClass (name); } clase protegida <?> findclass (nombre de cadena) lanza ClassNotFoundException {// Suponga que aquí es solo a un directorio específico que no sea el proyecto d: /biblioteca para cargar el código de implementación específico de clase omitido}}A través del análisis anterior, ya sabemos que el defensa predeterminado del cargador de clase definido por el usuario (WernClassLoader)
El cargador de clase reconocido es un cargador de clase de sistema, pero las conclusiones de los cuatro tipos de problemas no son válidas ahora. Todos pueden
Simplemente pruebelo, ahora <java_runtime_home>/lib, <java_runtime_home>/lib/ext y
Las clases en la ruta de la clase del programa no se pueden cargar.
Pregunta 5 Código de prueba 1
PublicClass WrongClassLoaderTest {publicStaticVoid Main (String [] args) {try {WrongClassLoader Loader = new WrongClassLoader (); Class ClassLoaded = Loader.LoadClass ("Beans.account"); System.out.println (classloaded.getName ()); System.out.println (classloaded.getClassLoader ()); } catch (Exception e) {E.PrintStackTrace (); }}}(Nota: D: cuenta de "clases" frijoles ". La clase existe físicamente)
Resultado de salida:
java.io.filenotfoundException: D: "clases" java "lang" objeto.class (el sistema no puede encontrar la ruta especificada.) En java.io.fileinputstream.open (método nativo) en java.io.fileinputstream. <itil> (fileinputstream.Java:106) en WrongClassLoader.FindClass (WrongClassLoader.java:40) en WrongClassLoader.LoadClass (WrongClassLoader.java:29) en java.lang.classasader.loadClassInternal (classloader.java:319) en java.lang.classasader.defineclass1 (método nativo) en el método nativo) en el método) en java.lang.classloader.defineeclass (classloader.java:620) en java.lang.classloader.defineeclass (classloader.java:400) WrongClassLoaderTest.Main (WrongClassLoaderTest.java:27) Excepción en el hilo "principal" java.lang.noclassDefFoundError: java/lang/objeto en java.lang.classloader.defineclass1 (método nativo) en java.lang.classascarer.defineclass (classLoaderer.Jav.Jav:620) java.lang.classloader.defineclass (classLoader.java:400) en WrongClassLoader.findclass (WrongClassLoader.java:43) en WrongClassLoader.LoadClass (WoChClassLoader.Java:29) en WrongClassLoaderTest.Main (WernClassLoaderTest.java:27)
Esto significa que incluso el objeto supertipo java.lang.object del tipo que se cargará no se puede cargar. Los errores lógicos enumerados aquí causados por sobrescribir la Clase de carga (...) obviamente son relativamente simples, y los errores lógicos causados de hecho pueden ser mucho más complicados.
Pregunta 5 Prueba 2
// LOADER CLASE CONSEJO DEL USER CLASE CLASECTER WORTCLASSLOCER.JAVA (no sobrescribe LoadClass Logic) publicClassWrongClassLoadExtends ClassLoader {Clase protegido <?> FindClass (Nombre de cadena) lanza ClassNotFoundException {// Suponía aquí es solo a un directorio específico que no sea el proyecto d: /biblioteca para cargar el código de implementación específico de clases omitido}}}}}}}}}}}}}}Después de modificar el código de cargador de clase personalizado WrongClassLoader.java, ejecute el código de prueba, y el resultado de salida es el siguiente:
frijoless.accountwrongclassloader@1c78e57
Esto muestra que los beans.account se carga correctamente y está cargado por el cargador de clase personalizado WernClassLoader.
No creo que sea necesario explicar las razones de esto, y debería poder analizarlo.
(2) Configure correctamente el cargador de clase principal a través del análisis de la pregunta 4 y la pregunta 5 anterior. Personalmente, creo que este es el punto más importante al personalizar los cargadores de clase de usuario, pero a menudo se ignora o se trae fácilmente. Con el análisis del código JDK anterior como base, creo que todos pueden dar cualquier ejemplo ahora.
(3) Asegúrese de que la corrección lógica del método FindClass (String), intente comprender con precisión las tareas de carga que el cargador de clases debe definir, y asegúrese de que el contenido de código de byto correspondiente se pueda obtener en la mayor medida.
6. ¿Cómo determinar qué rutas puede cargar el cargador de clase del sistema?
Primero, puede llamar directamente a ClassLoader.getSystemClassLoader () u otros métodos para obtener el cargador de clase del sistema (el cargador de clases del sistema y el cargador de clases de extensión se derivan de URLClassLoader), y puede obtenerlo llamando al método getURLS () en URLClassLoader;
En segundo lugar, puede ver directamente la información de entrada en el classpath actual obteniendo el atributo del sistema java.class.path. System.getProperty ("java.class.path")
7. ¿Cómo determinar qué rutas puede cargarse el cargador de clase de extensión estándar?
Método uno:
Pruebe {url [] ExtUrls = ((URLClassLoader) classLoader.getSystemClassLoader (). getParent ()). getUrls (); for (int i = 0; i <Exturls.length; i ++) {System.out.println (ExtUrls [i]); }} Catch (Exception e) {// ...}La salida correspondiente de esta máquina es la siguiente:
archivo:/d: /demo/jdk1.5.0_09/jre/lib/ext/dnsns.jarfile:/d: /demo/jdk1.5.0_09/jre/lib/ext/localedata.jarfile :/D: /demo/jdk1.5.0_09/jre/lib/ext/sunjce_provider.jarfile:/d: /demo/jdk1.5.0_09/jre/lib/ext/sunpkcs11.jar