2009-07-20 8 views
5

¿Cómo obtengo todas las clases dentro de un paquete?Java Package Introspection

+0

También tenga en cuenta [esta publicación] (http://stackoverflow.com/q/520328/1005481). –

Respuesta

10

No puede. Las clases pueden venir a través de muchos cargadores de diferentes clases, incluidos los remotos.

1

No existe una forma global de hacerlo. Dicho esto, si sabe de dónde provienen sus clases, puede recorrer el directorio de un archivo jar o el sistema de archivos.

3

Hay un fragmento de here que hace exactamente lo que quiere, suponiendo que las clases se pueden encontrar localmente :

private static Class[] getClasses(String packageName) 
throws ClassNotFoundException, IOException { 
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 
    assert classLoader != null; 
    String path = packageName.replace('.', '/'); 
    Enumeration<URL> resources = classLoader.getResources(path); 
    List<File> dirs = new ArrayList<File>(); 
    while (resources.hasMoreElements()) { 
     URL resource = resources.nextElement(); 
     dirs.add(new File(resource.getFile())); 
    } 
    ArrayList<Class> classes = new ArrayList<Class>(); 
    for (File directory : dirs) { 
     classes.addAll(findClasses(directory, packageName)); 
    } 
    return classes.toArray(new Class[classes.size()]); 
} 

private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException { 
    List<Class> classes = new ArrayList<Class>(); 
    if (!directory.exists()) { 
     return classes; 
    } 
    File[] files = directory.listFiles(); 
    for (File file : files) { 
     if (file.isDirectory()) { 
      assert !file.getName().contains("."); 
      classes.addAll(findClasses(file, packageName + "." + file.getName())); 
     } else if (file.getName().endsWith(".class")) { 
      classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6))); 
     } 
    } 
    return classes; 
} 
+0

¿Esto funciona con clases jar'ed? –

+0

Por lo que puedo decir, esto fallará si tiene un jar (-1) pero es una rutina decente para buscar archivos .class para el descubrimiento (+1) por lo que divide el valor :) –

+0

El código anterior no solo asume que las clases del paquete dado son locales, pero también que todas han sido cargadas por el mismo cargador de clases Y no han sido empaquetadas. – ChssPly76

1

Java no tiene descubrimiento.

La mayoría de los productos que tienen la capacidad de agregar (descubrir) nuevas clases tienen un archivo de texto que describe "Extensiones de programa" o un directorio específico donde puede ubicar clases o jarrones que utilizan un truco como @JG. (Esto es lo que hace eclipse y se recomienda para cualquier solución donde el usuario puede agregar el nuevo módulo a mano)

+1

No se olvide de las API de SPI para complementos. Estos proporcionan una solución razonablemente elegante para los marcos de plugins. – jsight

7

Aquí hay una forma más completa de resolver esto para jarras, según la idea publicada por JG.

/** 
* Scans all classloaders for the current thread for loaded jars, and then scans 
* each jar for the package name in question, listing all classes directly under 
* the package name in question. Assumes directory structure in jar file and class 
* package naming follow java conventions (i.e. com.example.test.MyTest would be in 
* /com/example/test/MyTest.class) 
*/ 
public Collection<Class> getClassesForPackage(String packageName) throws Exception { 
    String packagePath = packageName.replace(".", "/"); 
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 
    Set<URL> jarUrls = new HashSet<URL>(); 

    while (classLoader != null) { 
    if (classLoader instanceof URLClassLoader) 
     for (URL url : ((URLClassLoader) classLoader).getURLs()) 
     if (url.getFile().endsWith(".jar") // may want better way to detect jar files 
      jarUrls.add(url); 

    classLoader = classLoader.getParent(); 
    } 

    Set<Class> classes = new HashSet<Class>(); 

    for (URL url : jarUrls) { 
    JarInputStream stream = new JarInputStream(url.openStream()); // may want better way to open url connections 
    JarEntry entry = stream.getNextJarEntry(); 

    while (entry != null) { 
     String name = entry.getName(); 
     int i = name.lastIndexOf("/"); 

     if (i > 0 && name.endsWith(".class") && name.substring(0, i).equals(packagePath)) 
     classes.add(Class.forName(name.substring(0, name.length() - 6).replace("/", "."))); 

     entry = stream.getNextJarEntry(); 
    } 

    stream.close(); 
    } 

    return classes; 
} 
+0

-1 sin documentación ni siquiera sugerencia sobre lo que hace, +2 porque parece que escanea el classpath e incluye jarras! así que obtienes un +1 :) –

+0

(Bueno, había una pista sobre lo que hace ... Lo llevo de vuelta). –

+0

Agregó un encabezado javadoc para una mejor explicación. :) – toluju

1

Las respuestas de arriba describen la funcionalidad requerida. Sin embargo, se pueden encontrar más detalles sobre el tema here y here.

Cuestiones relacionadas