2012-06-13 10 views
49

Estoy tratando de hacer una herramienta Java que escanee la estructura de una aplicación Java y brinde información significativa. Para hacer esto, necesito poder escanear todos los archivos .class de la ubicación del proyecto (JAR/WAR o simplemente una carpeta) y usar la reflexión para leer sobre sus métodos. Esto está demostrando ser casi imposible.¿Cómo cargar clases en tiempo de ejecución desde una carpeta o JAR?

Puedo encontrar muchas soluciones basadas en URLClassloader que me permiten cargar clases específicas de un directorio/archivo, pero ninguna que me permita cargar clases sin tener ninguna información sobre el nombre de clase o la estructura del paquete.

EDITAR: Creo que lo expresé mal. Mi problema no es que no pueda obtener todos los archivos de clase, puedo hacerlo con recursividad, etc. y ubicarlos correctamente. Mi problema es obtener un objeto Class para cada archivo de clase.

+1

¿Cómo se puede cargar una clase ignorando su nombre completo? No creo que esto sea posible. –

+4

Las respuestas a continuación le permitirán ubicar los archivos de la clase pero, dependiendo del tamaño de su proyecto, podría valer la pena usar [asm] (http://asm.ow2.org/) o [javassist] (http: // www.csg.ci.iu-tokyo.ac.jp/~chiba/javassist/). Esto le permitirá analizar las clases sin cargarlas todas en la memoria. –

Respuesta

87

El siguiente código carga todas las clases de un archivo JAR. No necesita saber nada sobre las clases. Los nombres de las clases se extraen de JarEntry.

JarFile jarFile = new JarFile(pathToJar); 
Enumeration<JarEntry> e = jarFile.entries(); 

URL[] urls = { new URL("jar:file:" + pathToJar+"!/") }; 
URLClassLoader cl = URLClassLoader.newInstance(urls); 

while (e.hasMoreElements()) { 
    JarEntry je = e.nextElement(); 
    if(je.isDirectory() || !je.getName().endsWith(".class")){ 
     continue; 
    } 
    // -6 because of .class 
    String className = je.getName().substring(0,je.getName().length()-6); 
    className = className.replace('/', '.'); 
    Class c = cl.loadClass(className); 

} 

edición:

Como se sugiere en los comentarios anteriores, javassist también sería una posibilidad. Iniciar una ClassPool algún lugar antes de que el bucle while formar el código anterior, y en lugar de cargar la clase con el cargador de clases, se puede crear un objeto CtClass:

ClassPool cp = ClassPool.getDefault(); 
... 
CtClass ctClass = cp.get(className); 

Desde el ctClass, puede obtener todos los métodos, campos , clases anidadas, .... Echa un vistazo a la API javassist: https://jboss-javassist.github.io/javassist/html/index.html

+1

Perdón por abrir una publicación anterior (gracias por esta respuesta, por cierto, me funciona). Sin embargo, 'nueva URL (" jar: file: "+ pathToJar +"!/")' Provocó una 'ClassNotFoundException'. Lo que funcionó para mí fue: 'URL [] urls = {nueva URL (" jar: "+ pathToJar +"!/")};' ¿No estoy seguro de por qué? Espero que ayude a alguien más. – taylorcressy

+0

Agradable. ¿Podrías cerrar el jarFile sin embargo? try {...} finally {jarFile.close(); } –

+0

El enlace está muerto. –

6

para hacer esto, tengo que ser capaz de escanear todos los archivos .class de la ubicación del proyecto (JAR/WAR o solo una carpeta)

Escanear todos los archivos en una carpeta es simple. Una opción es llamar al File.listFiles() en el File que denota la carpeta, luego iterar la matriz resultante. Para atravesar árboles de carpetas anidadas, use la recursión.

El escaneo de los archivos de un archivo JAR se puede hacer utilizando la API JarFile ... y no es necesario volver a recurrir para atravesar "carpetas" anidadas.

Ninguno de estos es particularmente complicado. Solo lea el javadoc y comience a codificar.

8

Lista Todas las clases dentro del archivo jar.

public static List getClasseNames(String jarName) { 
    ArrayList classes = new ArrayList(); 

    if (debug) 
     System.out.println("Jar " + jarName); 
    try { 
     JarInputStream jarFile = new JarInputStream(new FileInputStream(
       jarName)); 
     JarEntry jarEntry; 

     while (true) { 
      jarEntry = jarFile.getNextJarEntry(); 
      if (jarEntry == null) { 
       break; 
      } 
      if (jarEntry.getName().endsWith(".class")) { 
       if (debug) 
        System.out.println("Found " 
          + jarEntry.getName().replaceAll("/", "\\.")); 
       classes.add(jarEntry.getName().replaceAll("/", "\\.")); 
      } 
     } 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    return classes; 
} 
Cuestiones relacionadas