Necesito leer las clases contenidas en un paquete de Java. Esas clases están en classpath. Necesito hacer esta tarea directamente desde un programa Java. ¿Conoces una forma sencilla de hacerlo?
List<Class> classes = readClassesFrom("my.package")
Necesito leer las clases contenidas en un paquete de Java. Esas clases están en classpath. Necesito hacer esta tarea directamente desde un programa Java. ¿Conoces una forma sencilla de hacerlo?
List<Class> classes = readClassesFrom("my.package")
Respuestas:
Si tiene Spring en su classpath, lo siguiente lo hará.
Busque todas las clases en un paquete que están anotadas con XmlRootElement:
private List<Class> findMyTypes(String basePackage) throws IOException, ClassNotFoundException
{
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
List<Class> candidates = new ArrayList<Class>();
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + "/" + "**/*.class";
Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
for (Resource resource : resources) {
if (resource.isReadable()) {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
if (isCandidate(metadataReader)) {
candidates.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
}
}
}
return candidates;
}
private String resolveBasePackage(String basePackage) {
return ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage));
}
private boolean isCandidate(MetadataReader metadataReader) throws ClassNotFoundException
{
try {
Class c = Class.forName(metadataReader.getClassMetadata().getClassName());
if (c.getAnnotation(XmlRootElement.class) != null) {
return true;
}
}
catch(Throwable e){
}
return false;
}
Puede utilizar el Proyecto Reflections descrito aquí
Es bastante completo y fácil de usar.
Breve descripción del sitio web anterior:
Reflections escanea su classpath, indexa los metadatos, le permite consultarlos en tiempo de ejecución y puede guardar y recopilar esa información para muchos módulos dentro de su proyecto.
Ejemplo:
Reflections reflections = new Reflections(
new ConfigurationBuilder()
.setUrls(ClasspathHelper.forJavaClassPath())
);
Set<Class<?>> types = reflections.getTypesAnnotatedWith(Scannable.class);
Yo uso este, funciona con archivos o archivos jar
public static ArrayList<String>getClassNamesFromPackage(String packageName) throws IOException{
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL packageURL;
ArrayList<String> names = new ArrayList<String>();;
packageName = packageName.replace(".", "/");
packageURL = classLoader.getResource(packageName);
if(packageURL.getProtocol().equals("jar")){
String jarFileName;
JarFile jf ;
Enumeration<JarEntry> jarEntries;
String entryName;
// build jar file name, then loop through zipped entries
jarFileName = URLDecoder.decode(packageURL.getFile(), "UTF-8");
jarFileName = jarFileName.substring(5,jarFileName.indexOf("!"));
System.out.println(">"+jarFileName);
jf = new JarFile(jarFileName);
jarEntries = jf.entries();
while(jarEntries.hasMoreElements()){
entryName = jarEntries.nextElement().getName();
if(entryName.startsWith(packageName) && entryName.length()>packageName.length()+5){
entryName = entryName.substring(packageName.length(),entryName.lastIndexOf('.'));
names.add(entryName);
}
}
// loop through files in classpath
}else{
URI uri = new URI(packageURL.toString());
File folder = new File(uri.getPath());
// won't work with path which contains blank (%20)
// File folder = new File(packageURL.getFile());
File[] contenuti = folder.listFiles();
String entryName;
for(File actual: contenuti){
entryName = actual.getName();
entryName = entryName.substring(0, entryName.lastIndexOf('.'));
names.add(entryName);
}
}
return names;
}
Spring ha implementado una excelente función de búsqueda de rutas de clases en PathMatchingResourcePatternResolver
. Si usa el classpath*
prefijo:, puede encontrar todos los recursos, incluidas las clases en una jerarquía determinada, e incluso filtrarlos si lo desea. Entonces puedes usar los hijos de AbstractTypeHierarchyTraversingFilter
,AnnotationTypeFilter
y AssignableTypeFilter
para filtrar esos recursos, ya sea en el nivel de anotaciones de clase o en las interfaces que implementan.
Java 1.6.0_24:
public static File[] getPackageContent(String packageName) throws IOException{
ArrayList<File> list = new ArrayList<File>();
Enumeration<URL> urls = Thread.currentThread().getContextClassLoader()
.getResources(packageName);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
File dir = new File(url.getFile());
for (File f : dir.listFiles()) {
list.add(f);
}
}
return list.toArray(new File[]{});
}
Esta solución se probó en el entorno EJB .
Scannotation y Reflections utilizan un enfoque de escaneo de rutas de clases:
Reflections reflections = new Reflections("my.package");
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);
Otro enfoque es utilizar la API de procesamiento de anotaciones Java Pluggable para escribir un procesador de anotaciones que recopilará todas las clases anotadas en tiempo de compilación y creará el archivo de índice para su uso en tiempo de ejecución. Este mecanismo se implementa en la biblioteca ClassIndex :
Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");
Hasta donde yo sé, esa funcionalidad todavía falta sospechosamente en la API de reflexión de Java. Puede obtener un objeto de paquete simplemente haciendo esto:
Package packageObj = Package.getPackage("my.package");
Pero como probablemente haya notado, eso no le permitirá enumerar las clases en ese paquete. A partir de ahora, debe adoptar un enfoque más orientado al sistema de archivos.
Encontré algunas implementaciones de muestra en esta publicación.
No estoy 100% seguro de que estos métodos funcionen cuando tus clases estén enterradas en archivos JAR, pero espero que uno de ellos lo haga por ti.
Estoy de acuerdo con @skaffman ... si tienes otra forma de hacer esto, te recomendaría hacerlo.
El mecanismo más robusto para listar todas las clases en un paquete dado es actualmente ClassGraph , porque maneja la más amplia gama posible de mecanismos de especificación de rutas de clases , incluido el nuevo sistema de módulos JPMS. (Yo soy el autor)
List<String> classNames;
try (ScanResult scanResult = new ClassGraph().whitelistPackages("my.package")
.enableClassInfo().scan()) {
classNames = scanResult.getAllClasses().getNames();
}
eXtcos parece prometedor. Imagina que quieres encontrar todas las clases que:
Con eXtcos esto es tan simple como
ClasspathScanner scanner = new ClasspathScanner();
final Set<Class> classStore = new ArraySet<Class>();
Set<Class> classes = scanner.getClasses(new ClassQuery() {
protected void query() {
select().
from(“common”).
andStore(thoseExtending(Component.class).into(classStore)).
returning(allAnnotatedWith(MyComponent.class));
}
});
Bill Burke ha escrito un (buen artículo sobre el escaneo de clases) y luego escribió Scannotation .
Hibernate ya tiene esto escrito:
CDI podría resolver esto, pero no lo sé, aún no lo he investigado completamente
.
@Inject Instance< MyClass> x;
...
x.iterator()
También para anotaciones:
abstract class MyAnnotationQualifier
extends AnnotationLiteral<Entity> implements Entity {}
Lo he implementado y funciona en la mayoría de los casos. Como es largo, lo puse en un archivo aquí. .
La idea es encontrar la ubicación del archivo fuente de la clase que está disponible en la mayoría de los casos (una excepción conocida son los archivos de clase JVM, hasta donde he probado). Si el código está en un directorio, escanee todos los archivos y solo detecte los archivos de clase. Si el código está en un archivo JAR, escanee todas las entradas.
Este método solo se puede utilizar cuando:
Tiene una clase que está en el mismo paquete que desea descubrir. Esta clase se llama SeedClass. Por ejemplo, si desea enumerar todas las clases en 'java.io', la clase semilla puede ser java.io.File
.
Sus clases están en un directorio o en un archivo JAR que tiene información de archivo fuente (no archivo de código fuente, sino solo archivo fuente). Por lo que he intentado, funciona casi al 100% excepto la clase JVM (esas clases vienen con la JVM).
Su programa debe tener permiso para acceder a ProtectionDomain de esas clases. Si su programa se carga localmente, no debería haber ningún problema.
He probado el programa solo para mi uso habitual, por lo que aún puede tener problemas.
Espero que esto ayude.
Aquí hay otra opción, una ligera modificación a otra respuesta arriba / abajo:
Reflections reflections = new Reflections("com.example.project.package",
new SubTypesScanner(false));
Set<Class<? extends Object>> allClasses =
reflections.getSubTypesOf(Object.class);
Cuando los applets eran un lugar común, se podía tener una URL en la ruta de clases. Cuando el cargador de clases requería una clase, buscaba todas las ubicaciones en la ruta de clases, incluidos los recursos http. Debido a que puede tener cosas como URL y directorios en la ruta de clases, no hay una manera fácil de obtener una lista definitiva de las clases.
Sin embargo, puedes acercarte bastante. Algunas de las bibliotecas de Spring están haciendo esto ahora. Puede obtener todos los archivos jar en la ruta de clases y abrirlos como archivos. Luego puede tomar esta lista de archivos y crear una estructura de datos que contenga sus clases.
utilizar maven de dependencia:
groupId: net.sf.extcos
artifactId: extcos
version: 0.4b
luego usa este código:
ComponentScanner scanner = new ComponentScanner();
Set classes = scanner.getClasses(new ComponentQuery() {
@Override
protected void query() {
select().from("com.leyton").returning(allExtending(DynamicForm.class));
}
});
Brent: la razón por la que la asociación es unidireccional tiene que ver con el hecho de que cualquier clase en cualquier componente de su CLASSPATH puede declararse en cualquier paquete (excepto java / javax). Por lo tanto, simplemente no hay mapeo de TODAS las clases en un "paquete" dado porque nadie sabe ni puede saber. Puede actualizar un archivo jar mañana y eliminar o agregar clases. Es como intentar obtener una lista de todas las personas llamadas John / Jon / Johan en todos los países del mundo: ninguno de nosotros es omnisciente, por lo tanto, ninguno de nosotros tendrá la respuesta correcta.