2010-10-02 18 views
5

Me gustaría obtener una lista de las clases que están disponibles en tiempo de ejecución y que coinciden con un nombre simple.Obteniendo una lista de nombres totalmente calificados con un nombre simple

Por ejemplo:

public List<String> getFQNs(String simpleName) { 
    ... 
} 

// Would return ["java.awt.List","java.util.List"] 
List<String> fqns = getFQNs("List") 

¿Hay una biblioteca que había de hacer esto de manera eficiente, o tengo que ir manualmente a través de todas las clases en cada cargador de clases? ¿Cuál sería la forma correcta de hacer eso?

Gracias!

ACTUALIZACIÓN

Una respuesta me preguntó por qué quería hacer esto. Esencialmente, quiero implementar una característica que sea similar a "organizar importaciones/importación automática", pero disponible en tiempo de ejecución. No me importa si la solución es relativamente lenta (especialmente si puedo construir una caché para que las consultas posteriores sean más rápidas) y si solo es un esfuerzo. Por ejemplo, no me importa si no obtengo clases generadas dinámicamente.

ACTUALIZACIÓN 2

tuviera que diseñar mi propia solución (ver más abajo): se utiliza algunos consejos proporcionados por los otros respondedores, pero me di cuenta de que tiene que ser extensible para manejar varios ambientes. No es posible recorrer automáticamente todos los cargadores de clases en tiempo de ejecución, por lo que debe confiar en estrategias generales y específicas de dominio para obtener una lista útil de clases.

+0

Ni siquiera estoy seguro de que pueda enumera las clases cargadas desde un cargador de clases. Esto podría ayudarlo http://media.techtarget.com/tss/static/articles/content/dm_loadedClasses/executing.pdf –

+1

@ Colin: incluso entonces, esto le permitiría encontrar clases que ya han sido cargadas por un cargador de clases, pero no las clases que están "disponibles en tiempo de ejecución". AFAIK no hay manera de saber si una clase determinada está disponible sin intentar cargarla. – Grodriguez

+0

@Grodriguez: tienes razón en que no hay forma de saberlo. Considere los cargadores de clase que generan bytecode sobre la marcha en tiempo de ejecución. –

Respuesta

4

Mezclé las respuestas de @Grodriguez y @bemace y agregué mi propia estrategia para llegar a una solución de mejor esfuerzo. Esta solución imita en el tiempo de ejecución la función de importación automática disponible en tiempo de compilación.

El código completo de my solution is here. Dado un nombre simple, los pasos principales son:

  1. obtener una lista de paquetes accesibles desde el cargador de clases actual.
  2. Para cada paquete, intente cargar el nombre completo calificado obtenido del paquete + nombre simple.

Paso 2 es fácil:

public List<String> getFQNs(String simpleName) { 
    if (this.packages == null) { 
     this.packages = getPackages(); 
    } 

    List<String> fqns = new ArrayList<String>(); 
    for (String aPackage : packages) { 
     try { 
      String fqn = aPackage + "." + simpleName; 
      Class.forName(fqn); 
      fqns.add(fqn); 
     } catch (Exception e) { 
      // Ignore 
     } 
    } 
    return fqns; 
} 

Paso 1 es más difícil y depende de su aplicación/medio ambiente por lo que he implementado diversas estrategias para obtener diferentes listas de paquetes.

cargador de clases actual (puede ser útil para detectar clases generadas dinámicamente)

public Collection<String> getPackages() { 
    Set<String> packages = new HashSet<String>(); 
    for (Package aPackage : Package.getPackages()) { 
     packages.add(aPackage.getName()); 
    } 
    return packages; 
} 

Classpath (lo suficientemente bueno para aplicaciones que se cargan por completo de la ruta de clase. No es bueno para aplicaciones complejas como Eclipse)

public Collection<String> getPackages() { 
    String classpath = System.getProperty("java.class.path"); 
    return getPackageFromClassPath(classpath); 
} 

public static Set<String> getPackageFromClassPath(String classpath) { 
    Set<String> packages = new HashSet<String>(); 
    String[] paths = classpath.split(File.pathSeparator); 
    for (String path : paths) { 
     if (path.trim().length() == 0) { 
      continue; 
     } else { 
      File file = new File(path); 
      if (file.exists()) { 
       String childPath = file.getAbsolutePath(); 
       if (childPath.endsWith(".jar")) { 
        packages.addAll(ClasspathPackageProvider 
          .readZipFile(childPath)); 
       } else { 
        packages.addAll(ClasspathPackageProvider 
          .readDirectory(childPath)); 
       } 
      } 
     } 

    } 
    return packages; 
} 

Bootstrap ruta de clase (por ejemplo, java.lang)

public Collection<String> getPackages() { 
    // Even IBM JDKs seem to use this property... 
    String classpath = System.getProperty("sun.boot.class.path"); 
    return ClasspathPackageProvider.getPackageFromClassPath(classpath); 
} 

paquetes Eclipse (proveedor de paquetes de dominio específico)

// Don't forget to add "Eclipse-BuddyPolicy: global" to MANIFEST.MF 
public Collection<String> getPackages() { 
    Set<String> packages = new HashSet<String>(); 
    BundleContext context = Activator.getDefault().getBundle() 
      .getBundleContext(); 
    Bundle[] bundles = context.getBundles(); 
    PackageAdmin pAdmin = getPackageAdmin(context); 

    for (Bundle bundle : bundles) { 
     ExportedPackage[] ePackages = pAdmin.getExportedPackages(bundle); 
     if (ePackages != null) { 
      for (ExportedPackage ePackage : ePackages) { 
       packages.add(ePackage.getName()); 
      } 
     } 
    } 

    return packages; 
} 

public PackageAdmin getPackageAdmin(BundleContext context) { 
    ServiceTracker bundleTracker = null; 
    bundleTracker = new ServiceTracker(context, 
      PackageAdmin.class.getName(), null); 
    bundleTracker.open(); 
    return (PackageAdmin) bundleTracker.getService(); 
} 

ejemplos de preguntas y respuestas en mi entorno Eclipse:

  1. del archivo: [java.io.File, org.eclipse.core.internal.resources.File]
  2. Lista: [java.awt.List, org.eclipse.swt.widgets.List, com.sun.xml.internal.bind.v2.schemage n.xmlschema.List, java.util.List, org.hibernate.mapping.List]
  3. IResource: [org.eclipse.core.resources.IResource]
1

Probablemente no pueda hacer esto en absoluto. No hay forma de que la JVM sepa si una clase List en un paquete denominado arbitrariamente a.b.c.d está disponible sin intentar cargar a.b.c.d.List primero. Debería probar todos los posibles nombres de paquete.

1

Sin cargarlos todo lo que podía obtener la propiedad ruta de clase

String class_path = System.getProperty("java.class.path"); 

Y entonces se podría crear una función para buscar el sistema de ficheros de clases en esos lugares. Y tendría que codificarlo para que también busque dentro de archivos jar, y algunas de las clases pueden no estar disponibles actualmente debido a incompatibilidades que solo se revelarían cuando las cargue. Pero si solo quieres una mejor estimación de lo que está disponible, esto podría ser viable. Tal vez debería decirnos por qué desea hacer esto para que pueda obtener algunas sugerencias alternativas?

Editar: Ok, suena como usted debe comprobar fuera de este hilo y los vinculados en ella: How do I read all classes from a Java package in the classpath? En particular, parece que el marco de la primavera hace algo similar, tal vez se puede ver en ese código: http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/core/io/support/PathMatchingResourcePatternResolver.html

Cuestiones relacionadas