web-dev-qa-db-fra.com

Comment lire toutes les classes d'un package Java dans le chemin de classe?

J'ai besoin de lire les classes contenues dans un package Java. Ces classes sont classpath. J'ai besoin de faire cette tâche à partir d'un programme Java directement. Connaissez-vous un moyen simple de faire?

List<Class> classes = readClassesFrom("my.package")
90
user176803

Si vous avez Spring dans votre classpath, ce qui suit le fera.

Recherchez toutes les classes d'un package annotées avec 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;
}
46
Shalom938

Vous pouvez utiliser le projet Reflections décrit ici

C'est assez complet et facile à utiliser.

Brève description du site Web ci-dessus:

Reflections analyse votre chemin de classe, indexe les métadonnées, vous permet de interrogez-le au moment de l'exécution et sauvegardez et collectez ces informations pour plusieurs modules au sein de votre projet.

Exemple:

Reflections reflections = new Reflections(
    new ConfigurationBuilder()
        .setUrls(ClasspathHelper.forJavaClassPath())
);
Set<Class<?>> types = reflections.getTypesAnnotatedWith(Scannable.class);
29
Renato

J'utilise celui-ci, il fonctionne avec des fichiers ou des archives de bocaux

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;
}
26
Edoardo Panfili

Spring a implémenté une excellente fonction de recherche de classpath dans la variable PathMatchingResourcePatternResolver . Si vous utilisez le préfixe classpath*:, vous pouvez rechercher toutes les ressources, y compris les classes d'une hiérarchie donnée, et même les filtrer si vous le souhaitez. Vous pouvez ensuite utiliser les enfants de AbstractTypeHierarchyTraversingFilter , AnnotationTypeFilter et AssignableTypeFilter pour filtrer ces ressources soit sur des annotations de niveau classe, soit sur des interfaces qu'elles implémentent.

11
John Ellinwood

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[]{});
}

Cette solution a été testée dans l’environnement EJB .

6
Paul Kuit

Scannotation et Reflections utilisent l'approche de balayage de chemin de classe:

Reflections reflections = new Reflections("my.package");
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);

Une autre approche consiste à utiliser API de traitement d'annotations Java connectable pour écrire un processeur d'annotation qui collectera toutes les classes annotées au moment de la compilation et construira le fichier d'index pour une utilisation à l'exécution. Ce mécanisme est implémenté dans ClassIndex library:

Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");
6
Sławek

Autant que je sache, cette fonctionnalité manque encore de manière suspecte dans l'API de réflexion Java. Vous pouvez obtenir un objet package simplement en procédant comme suit:

Package packageObj = Package.getPackage("my.package");

Mais comme vous l'avez probablement remarqué, cela ne vous laissera pas lister les classes de ce paquet. À l'heure actuelle, vous devez adopter une approche plus orientée système de fichiers.

J'ai trouvé quelques exemples d'implémentations dans this post 

Je ne suis pas sûr à 100% que ces méthodes fonctionneront lorsque vos cours seront enterrés dans des fichiers JAR, mais j'espère que l'un d'entre eux le fera pour vous.

Je suis d'accord avec @skaffman ... si vous avez une autre façon de procéder, je vous recommanderais de le faire à la place.

3
Brent Writes Code

Vous pouvez essayer ma bibliothèque FastClasspathScanner . Analyser le chemin de classe n’est pas aussi simple que de vérifier la propriété Java.class.path et d’y rechercher des classes de manière récursive, car il existe plusieurs façons de spécifier le chemin de classe (par exemple, vous pouvez ajouter des entrées Class-Path au manifeste d’un fichier jar). Utiliser Class.forName () initialise effectivement la classe, ce qui peut ne pas être ce que vous voulez, etc. FastClasspathScanner gère ces complexités pour vous.

Pour énumérer toutes les classes d'un package, procédez comme suit:

String packageName = "my.package";
Set<String> classNames = new FastClassPathScanner(packageName)
    .scan()
    .getNamesOfAllClasses();
String packageNamePrefix = packageName + ".";
for (String className : classNames) {
    if (className.startsWith(packageNamePrefix) {
        System.out.println("Found class: " + className);
   }
}

Vous avez besoin de la case à cocher if (className.startsWith(packageNamePrefix) car si une classe fait référence à une autre classe (par exemple, une superclasse) et qu’elle se situe en dehors du préfixe du paquet sur liste blanche my.package, elle sera incluse dans l’ensemble renvoyé par .getNamesOfAllClasses().

3
Luke Hutchison
  1. Bill Burke a écrit un (Nice article sur le balayage de classe], puis il a écrit Scannotation.

  2. Hibernate a déjà écrit ceci:

    • org.hibernate.ejb.packaging.Scanner
    • org.hibernate.ejb.packaging.NativeScanner
  3. CDI pourrait résoudre ce problème, mais ne sait pas - n'a pas encore enquêté à fond 

.

@Inject Instance< MyClass> x;
...
x.iterator() 

Aussi pour les annotations:

abstract class MyAnnotationQualifier
extends AnnotationLiteral<Entity> implements Entity {}
2
Ondra Žižka

eXtcos semble prometteur. Imaginez que vous souhaitiez trouver toutes les classes qui:

  1. Étendre de la classe "Composant" et les stocker
  2. Sont annotés avec "MyComponent", et
  3. Sont dans le paquet "commun". 

Avec eXtcos, c'est aussi simple que

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

Je l’ai implémenté et cela fonctionne dans la plupart des cas. Comme il est long, je le mets dans un fichier ici .

L'idée est de trouver l'emplacement du fichier source de la classe qui est disponible dans la plupart des cas (une exception connue concerne les fichiers de classe JVM - pour autant que j'ai testé). Si le code est dans un répertoire, parcourez tous les fichiers et uniquement les fichiers de classe Spot. Si le code est dans un fichier JAR, analysez toutes les entrées.

Cette méthode ne peut être utilisée que lorsque:

  1. Vous avez une classe qui se trouve dans le même package que vous souhaitez découvrir. Cette classe s'appelle une SeedClass. Par exemple, si vous souhaitez répertorier toutes les classes dans 'Java.io', la classe de départ peut être Java.io.File.

  2. Vos classes se trouvent dans un répertoire ou dans un fichier JAR. Elles contiennent des informations sur le fichier source (pas le fichier de code source, mais uniquement le fichier source). Pour autant que j'ai essayé, cela fonctionne presque à 100% sauf la classe JVM (ces classes sont livrées avec la JVM).

  3. Votre programme doit avoir l'autorisation d'accéder à ProtectionDomain de ces classes. Si votre programme est chargé localement, il ne devrait y avoir aucun problème.

J'ai testé le programme uniquement pour mon utilisation habituelle, donc il peut toujours avoir un problème.

J'espère que ça aide.

1
NawaMan

Voici une autre option, légère modification d'une autre réponse en haut/en bas:

Reflections reflections = new Reflections("com.example.project.package", 
    new SubTypesScanner(false));
Set<Class<? extends Object>> allClasses = 
    reflections.getSubTypesOf(Object.class);
1
infinity

Le projet ldapbeans fournit une classe Scanner qui le fait.

0
Stephan

utiliser dépendance maven: 

groupId: net.sf.extcos
artifactId: extcos
version: 0.4b

puis utilisez ce code: 

ComponentScanner scanner = new ComponentScanner();
        Set classes = scanner.getClasses(new ComponentQuery() {
            @Override
            protected void query() {
                select().from("com.leyton").returning(allExtending(DynamicForm.class));
            }
        });
0
Yalami

Si vous utilisez déjà Guava, vous pouvez utiliser ClassPath. Découvrez la documentation ici: https://google.github.io/guava/releases/17.0/api/docs/com/google/common/reflect/ClassPath.html

0
satnam

À l'époque où les applets étaient monnaie courante, on pouvait avoir une URL sur le classpath. Lorsque le chargeur de classes a besoin d'une classe, il recherche tous les emplacements sur le chemin d'accès aux classes, y compris les ressources http. Comme vous pouvez avoir des éléments tels que des URL et des répertoires sur le chemin de classe, il n’existe pas de moyen simple d’obtenir une liste définitive des classes.

Cependant, vous pouvez être assez proche. Certaines bibliothèques de Spring le font maintenant. Vous pouvez obtenir tous les fichiers jar sur le classpath et les ouvrir comme des fichiers. Vous pouvez ensuite utiliser cette liste de fichiers et créer une structure de données contenant vos classes.

0
brianegge