Desmitificando los ClassLoaders de Java
Cuando programamos en Java sabemos que nuestras clases se pueden ejecutar en distintas plataformas sin tener que modificarlas gracias a la existencia de la máquina virtual, pero no nos paramos a pensar como funciona esa sinergia entre clases y máquina virtual. ¿Cómo, Cuándo, Desde donde lee nuestras clases la JVM?. ¡¡¡La respuesta me la sé!!!, vaya pregunta mas fácil: desde el classpath que configuramos al arrancar la aplicación.
Pues sí, eso es parcialmente cierto, pero la respuesta no es tan obvia, ¿Qué pasa con los Applets?, ¿Y con Java Web Start?, ¿Por qué los servidores de aplicaciones como Tomcat, Jetty, JBoss, Glassfish, etc pueden permitirse el lujo de cargar las clases que dejamos en la carpeta WEB-INF/classes?. Esta sinergia totalmente personalizable entre clases y maquina virtual existe gracias a los ClassLoaders. Entonces…
¿Qué es un ClassLoader?
Un ClassLoader es una instancia de java.lang.ClassLoader responsable de cargar clases en memoria de una determinada manera, bajo demanda de la JVM.
Para la máquina virtual una clase se identifica por su nombre completo paquetex.paquetey.Clase y su ClassLoader. De está manera la clase es.masterd.SoyUnaClase cargada con el cargador MasterDClassLoaderCommon no es la misma clase que es.masterd.SoyUnaClase cargada con el cargador MasterDClassLoaderApplication.
Por defecto y en la mayoría de los casos, la máquina virtual carga las clases definidas en el classpath por medio del System ClassLoader. Pero nosotros podemos utilizar otro ClassLoader o incluso implementar uno nuevo y cargar las clases, por ejemplo, desde una URL remotamente.
Si nos paramos a pensar un momento, podemos intuir que aparte de ofrecerte un potencial enorme también te ofrece un grave fallo de seguridad. Podría programarme una clase maliciosa java.util.ArrayList diferente a la ya implementada en la JDK, inyectarla en el classpath y modificar su comportamiento a mi favor. Pero como ya sabemos, esto no pasa. ¿Por qué?.
Módelo de delegación de ClassLoaders
Existen tres ClassLoaders por defecto en Java:
- Bootstrap ClassLoader: Carga las clases principales de java como java.*, javax.*. Está implementado en C para cada plataforma, forma parte de la JVM.
- Extension ClassLoader: Carga las clases proporcionadas en el directorio de extensiones del JRE.
- System ClassLoader: También llamado comúnmente Application ClassLoader, carga las clases definidas en el classpath, las nuestras.
Cada ClassLoader contiene la referencia de su ClassLoader padre, formando así una Jerarquía de árbol, excepto Bootstrap ClassLoader que es la raíz del árbol. Por defecto, el padre de un ClassLoader implementado por nosotros es System ClassLoader pudiendo cambiarlo al que más nos interese. Aquí muestro un ejemplo visual de una Jerarquía de ClassLoaders:
Los ClassLoaders, por defecto, delegan siempre la carga de la clase a su padre, recursivamente hasta llegar al Bootstrap ClassLoader, el origen de todos los ClassLoaders. Comprueba si ya está cargada y si no lo está, la intenta cargar. Si no puede la devuelve a su hijo, invirtiendo el proceso, hasta que un ClassLoader la cargue. Si ningún ClassLoader puede cargar la clase se lanzará la excepción java.lang.ClassNotFoundException.
De esta manera, se evita la sustitución maliciosa de clases base de la JRE. Aunque inyectáramos un java.util.ArrayList distinto en nuestro classpath, System ClassLoader delegará la carga a Extension ClassLoader y este a su vez la delegará a Bootstrap ClassLoader que encontrará que ya tiene una clase cargada con ese nombre (la original) y no cargará la clase mal intencionada. El siguiente código de la clase java.lang.ClassLoader lo explica perfectamente.
Hecha la ley, hecha la trampa. Cuando programas un ClassLoader personalizado es recomendable usar este módelo basado en delegación, pero puedes no hacerlo. En este caso deberías asegurarte que si es una clase de sistema, se cargue con el System ClassLoader para evitar problemas. Existe un claro ejemplo que no usa el módelo de delegación: Los servidores de aplicaciones web. Ya que así lo recomienda la especificación de Java Servlet.
Todo esto está muy bien, pero me surge una duda…
¿Para qué me sirve programar un ClassLoader?
Se suele programar un ClassLoader en Java para poder obtener una clase de una forma distinta a la normal. Permiten hacer cosas como:
- Cargar y Descargar clases dinámicamente cuando se detecta un cambio.
- Ejecutar lenguajes de script.
- Implementar un sistema de plugins en tiempo de ejecución.
- Cargar las clases remotamente, por ejemplo, desde un LDAP.
- Cargar clases cifradas.
- Modificar el bytecode de la clase en tiempo de carga. Programación Orientada a Aspectos.
- Crear una jerarquía de cargadores, para aislar distintas aplicaciones en una misma JVM.
Conclusión
Los arquitectura de ClassLoaders de Java ofrece a los programadores una tremenda flexibilidad para extender y ensamblar nuestras aplicaciones. Por eso, un desarrollador Java debería entender la carga de clases al detalle. En este artículo se ha realizado una introducción a los conceptos básicos de los ClassLoaders.
En mi siguiente post explicaré como programar un ClassLoader a medida. En este post hay mucha teoría, necesaria por otra parte, y como se entiende mejor los conceptos es con un ejemplo práctico.
Saludos.

March 19th, 2009 at 6:42 pm
He visto utilizar class loaders para algunas cosas que comentas: cifrar clases y cargar plugins. Lo de cifrar las clases tiene su problema: que el ClassLoader que carga las clases cifradas no puede estar cifrado
Pero bueno, siempre es una barrera más de seguridad. Nada es seguro 100%
Muy buen artículo!
March 20th, 2009 at 6:29 pm
Muy bien explicado, pondré un ejemplo de classloader que hice hace tiempos para empaquetar una aplicación con las clases que solo necesita. Hoy en día lo haríamos con un ofuscador (yguard,etc), pero si controlamos todas las clases necesarias y todos los casos de uso, obtendríamos un nuevo jar con solo las dependencias indispensables.
Un saludo!
February 22nd, 2010 at 9:12 pm
Hola,
estaría muy interesado en saber como descargar un JAR del SystemClassLoader.
Los cargo correctamente (addURL) y todo va bien, pero me interesaría poder descargarlos luego y no encuentro la forma.
Me podeis ayudar?
gracias
May 31st, 2010 at 5:45 am
hey, muy bien explicado, mis agracedimientos porque esto me a ayudado a comprender mejor eso del classLoader, voy a estar antento al otro post, y sino porfavor avisenme…