java class reflection jar load

java - ¿Cómo cargar clases en tiempo de ejecución desde una carpeta o JAR?



class reflection (3)

Para hacer esto, necesito poder escanear todos los archivos .class desde la ubicación del proyecto (JAR / WAR o simplemente una carpeta)

Escanear todos los archivos en una carpeta es simple. Una opción es llamar a 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 de JarFile ... y no es necesario recurrir para atravesar "carpetas" anidadas.

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

Intento crear 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.

Puedo encontrar muchas soluciones basadas en URLClassloader que me permiten cargar clases específicas desde 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 formulé esta frase 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.


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); }

editar:

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

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

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


Enumera 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; }