Consejo 105 de Java: Dominar la ruta de clases con JWhich

En un momento u otro, los desarrolladores se sienten frustrados al tratar con la ruta de clases de Java. No siempre está claro qué clase cargará el cargador de clases, especialmente cuando la ruta de clases de su aplicación se inunda con directorios y archivos. En este artículo, presentaré una herramienta que puede mostrar el nombre de ruta absoluto del archivo de clase cargado.

Conceptos básicos de Classpath

La máquina virtual Java (JVM) emplea un cargador de clases para cargar las clases utilizadas por una aplicación según sea necesario. La CLASSPATHvariable de entorno le dice al cargador de clases dónde encontrar clases de terceros y definidas por el usuario. También puede especificar la ruta de clase por aplicación con el -classpathargumento de la línea de comandos de JVM, que anula la ruta de clase especificada en la CLASSPATHvariable de entorno.

Las entradas de classpath pueden ser directorios que contienen archivos de clases para clases que no están en un paquete, el directorio raíz del paquete para clases en un paquete o archivos de almacenamiento (como archivos .zip o .jar) que contienen clases. Las entradas de classpath están separadas por dos puntos en los sistemas de tipo Unix y por punto y coma en los sistemas MS Windows.

Los cargadores de clases se organizan en una jerarquía de delegación, y cada cargador de clases tiene un cargador de clases padre. Cuando se le pide a un cargador de clases que busque una clase, primero delega la solicitud a su cargador de clases padre antes de intentar encontrar la clase en sí. El cargador de clases del sistema, el cargador de clases predeterminado proporcionado por el JDK o JRE instalado en su sistema, carga clases de terceros y definidas por el usuario utilizando la CLASSPATHvariable de entorno o el -classpathargumento de línea de comandos de JVM. El cargador de clases del sistema delega en la clase de extensión para cargar clases que utilizan el mecanismo de extensión de Java. El cargador de clases de extensión delega en el cargador de clases de arranque (¡el dinero se detiene aquí!) Para cargar las clases principales de JDK.

Puede desarrollar cargadores de clases especializados para personalizar cómo la JVM carga las clases dinámicamente. Por ejemplo, la mayoría de los motores de servlets utilizan un cargador de clases personalizado para recargar dinámicamente las clases de servlets que han cambiado en los directorios especificados en una ruta de clases personalizada.

De particular importancia, y mucha consternación, el cargador de clases cargará las clases en el orden en que aparecen en la ruta de clases. Comenzando con la primera entrada de classpath, el cargador de clases visita cada directorio o archivo de almacenamiento especificado para intentar encontrar la clase para cargar. Se carga la primera clase que encuentra con el nombre adecuado y se ignoran las entradas de classpath restantes.

Suena simple, ¿verdad?

Truco de classpath

Ya sea que lo admitan o no, tanto los desarrolladores de Java principiantes como los veteranos han sido engañados en algún momento (¡generalmente en el peor momento posible!) Por el oneroso classpath. A medida que aumenta el número de clases dependientes de terceros y definidas por el usuario para una aplicación, y la ruta de clases se convierte en un vertedero para todos los directorios y archivos de almacenamiento imaginables, no siempre es obvio qué clase cargará primero el cargador de clases. Esto es especialmente cierto en el desafortunado caso de que la ruta de clases contenga entradas de clase duplicadas. Recuerde, el cargador de clases carga la primera clase con el nombre correcto que encuentra en la ruta de clases y "oculta" efectivamente todas las demás clases de menor precedencia con el nombre correcto.

Es muy fácil ser víctima de este engaño de classpath. Después de un largo día de trabajar como esclavo con un teclado activo, agrega un directorio a la ruta de clases en un intento de obtener la última y mejor versión de una clase cargada en la aplicación, sin saber que otra versión de la clase se encuentra en un directorio de mayor precedencia en la ruta de clases. ¡Te tengo!

JWhich: una herramienta de ruta de clases simple

El problema de precedencia inherente a una declaración de ruta plana no es exclusivo de la ruta de clases de Java. Para encontrar una solución al problema solo se requiere que se apoye sobre los hombros de los legendarios gigantes del software. El whichcomando del sistema operativo Unix toma un nombre y muestra la ruta del archivo que se ejecutaría si el nombre se hubiera emitido como comando. Básicamente, atraviesa la PATHvariable de entorno para localizar la primera aparición del comando. Eso también suena como una herramienta poderosa para administrar la ruta de clases de Java. Inspirado por esa noción, me puse a escribir una utilidad Java que pudiera tomar un nombre de clase Java y mostrar el nombre de ruta absoluto del archivo de clase que cargaría el cargador de clases, según lo prescrito por la ruta de clases.

El siguiente ejemplo de uso de JWhichmuestra el nombre de ruta absoluto de la primera aparición de la com.clarkware.ejb.ShoppingCartBeanclase que cargará el cargador de clases, que se encuentra en un directorio:

 > java JWhich com.clarkware.ejb.ShoppingCartBean Clase 'com.clarkware.ejb.ShoppingCartBean' encontrado en '/home/mclark/classes/com/clarkware/ejb/ShoppingCartBean.class' 

El siguiente ejemplo de uso de JWhichmuestra el nombre de ruta absoluto de la primera aparición de la javax.servlet.http.HttpServletclase que cargará el cargador de clases, que está empaquetado en un archivo de almacenamiento:

 > java JWhich javax.servlet.http.HttpServlet Clase 'javax.servlet.http.HttpServlet' encontrada en 'archivo: /home/mclark/lib/servlet.jar! /javax/servlet/http/HttpServlet.class' 

Cómo funciona JWhich

Para determinar sin ambigüedades qué clase se cargará primero en la ruta de clases, debe entrar en la mente del cargador de clases. Esto no es tan difícil como parece, ¡solo pídelo! El código fuente relevante para lo JWhichsiguiente. Para obtener el código fuente completo, consulte Recursos.

1: public class JWhich {2: 3: / ** 4: * Imprime el nombre de ruta absoluto del archivo de clase 5: * que contiene el nombre de clase especificado, según lo prescrito 6: * por la ruta de clase actual. 7: * 8: * @param className Nombre de la clase. 9: * / 10: public static void which (String className) {11: 12: if (! ClassName.startsWith ("/")) {13: className = "/" + className; 14:} 15: className = className.replace ('.', '/'); 16: className = className + ".class"; 17: 18: java.net.URL classUrl = 19: new JWhich (). GetClass (). GetResource (className); 20: 21: if (classUrl! = Null) {22: System.out.println ("\ nClass '" + className + 23: "' encontrado en \ n '" + classUrl.getFile () + "'"); 24:} else {25: System.out.println ("\ nClass '" + className + 26: "' no encontrado en \ n '"+ 27: System.getProperty (" java.class.path ") +" '"); 28:} 29:} 30: 31: public static void main (String args []) {32: if (args.length > 0) {33: JWhich.which (args [0]); 34:} else {35: System.err.println ("Uso: java JWhich"); 36:} 37:} 38:}

Primero, necesita masajear un poco el nombre de la clase para obtener la aceptación del cargador de clases (líneas 12-16). Anteponer una "/" al nombre de la clase se indica al cargador de clases que coincida literalmente con el nombre de la clase dentro de la ruta de clase, en lugar de intentar anteponer implícitamente el nombre del paquete de la clase que invoca. Convertir cada aparición de "." a "/" formatea el nombre de la clase como un nombre de recurso de URL válido requerido por el cargador de clases.

A continuación, se interroga al cargador de clases (líneas 18-19) para el recurso que coincide con el nombre de clase con el formato adecuado. Cada Classobjeto mantiene una referencia al ClassLoaderobjeto que lo cargó, por lo que el cargador de clases que cargó la JWhichclase en sí se interroga aquí. El Class.getResource()método realmente delega al cargador de clases que cargó la clase, devolviendo una URL para leer el recurso del archivo de clase, o nullsi un recurso del archivo de clase con el nombre de clase especificado no se pudo encontrar en la ruta de clase actual.

Finalmente, se muestra la ruta absoluta del archivo de clase que contiene el nombre de clase especificado, si se encontró en la ruta de clase actual (líneas 21-24). Como ayuda para la depuración, si el archivo de clase no se encontró en la ruta de clases actual, obtiene el valor de la java.class.pathpropiedad del sistema para mostrar la ruta de clases actual (líneas 24-28).

Es fácil imaginar cómo este simple fragmento de código podría invocarse en un servlet Java usando la ruta de clase del motor de servlet o un Enterprise JavaBean (EJB) usando la ruta de clase del servidor EJB. Si la JWhichclase fuera cargada por el cargador de clases personalizado en un motor de servlets, por ejemplo, entonces el cargador de clases del motor de servlets se usaría para buscar clases. Si el cargador de clases del motor de servlets no puede ubicar una clase, la delegará en su cargador de clases padre. En general, cuando JWhiches cargado por un cargador de clases, puede encontrar todas las clases cargadas por su cargador de clases o cualquier cargador de clases padre.

Conclusión

Si la necesidad es la madre de todos los inventos, hace mucho que se necesita una herramienta que ayude a administrar la ruta de clases de Java. Los grupos de noticias y las listas de correo relacionados con Java están repletos de preguntas relacionadas con la ruta de clases. Necesitamos reducir la barrera de entrada para nuevos desarrolladores para que todos podamos seguir trabajando en niveles más altos de abstracción. JWhiches una herramienta simple pero poderosa que lo ayudará a dominar la ruta de clases de Java en cualquier entorno.

Mike Clark es consultor independiente de Clarkware Consulting, especializado en arquitectura, diseño y desarrollo basados ​​en Java utilizando tecnologías J2EE. Recientemente completó el desarrollo y la implementación de un servidor de intercambio XML de empresa a empresa (B2B) y actualmente es consultor para un proyecto de creación de un producto de gestión del rendimiento J2EE.

Más información sobre este tema

  • Obtain the full source code for this article

    //images.techhive.com/downloads/idge/imported/article/jvw/2000/12/jwhich.zip

  • A full-featured version of JWhich, including a classpath validator, is available at

    //www.clarkware.com/software/jwhich.zip

  • Official documentation for the Sun JDK and how it deals with the classpath for the various officially supported platforms is available at

    //java.sun.com/j2se/1.3/docs/tooldocs/findingclasses.html

  • For details on how to set the classpath on Unix and Windows platforms, see "Setting the classpath" at:
  • Unix

    //java.sun.com/j2se/1.3/docs/tooldocs/solaris/classpath.html

  • Windows

    //java.sun.com/j2se/1.3/docs/tooldocs/win32/classpath.html

  • View all previous Java Tips and submit your own

    //www.javaworld.com/javatips/jw-javatips.index.html

  • Para obtener más trucos de Java, suscríbase al boletín informativo gratuito Java Tutor de ITworld.com

    //www.itworld.com/cgi-bin/subcontent12.cgi

  • Hable en la discusión para principiantes de Java, moderada por el autor de JavaWorld , Geoff Friesen

    //www.itworld.com/jump/jw-javatip105/forums.itworld.com/[email protected]@.ee6b804/1195!skip=1125

Esta historia, "Java Tip 105: Dominar la ruta de clases con JWhich" fue publicada originalmente por JavaWorld.