33 votes

Comment puis-je énumérer toutes les classes d'un package et les ajouter à une liste?

J'ai besoin d'énumérer toutes les classes dans un package et les ajouter à une Liste. Le non-version dynamique pour une seule classe qui va comme ceci:

List allClasses = new ArrayList();
allClasses.add(String.class);

Comment puis-je le faire dynamiquement pour ajouter toutes les classes dans un package et tous ses sous-paquets?


Mise à jour: après Avoir lu le début de réponses, il est absolument vrai que j'essaie de résoudre un autre problème secondaire, alors permettez-moi de le dire. Et je sais que c'est possible puisque d'autres outils de le faire. Voir la nouvelle question ici.

Mise à jour: la Lecture de ce nouveau, je peux voir comment c'est d'être mal interprétées. Je suis à la recherche d'énumérer l'ensemble de MON PROJET DE cours à partir du système de fichiers après la compilation.

39voto

Dave Dopson Points 16690

*Mise à JOUR*

OK, j'ai enfin eu le temps de faire le ménage dans l'extrait de code ci-dessous. Je l'ai coincé dans son propre projet github et même l'ajout de tests.

https://github.com/ddopson/java-class-enumerator

*Post Original*

Strictement parlant, il n'est pas possible de dresser la liste des classes dans un package. C'est parce qu'un package est vraiment rien de plus qu'un espace de noms (par exemple com.epicapplications.foo.bar), et tout le jar fichier dans le chemin de classe pourrait éventuellement ajouter des classes dans un package. Pire encore, le chargeur de classe classes de charge sur demande, et une partie du classpath peut-être de l'autre côté d'une connexion réseau.

Il est possible de résoudre un plus restrictive problème. par exemple, toutes les classes dans un fichier JAR, ou toutes les classes d'un fichier JAR, définit à l'intérieur d'un paquet particulier. C'est le plus souvent de toute façon.

Malheureusement, il n'existe pas de code de la structure pour rendre cette tâche facile. Vous devez analyser le système de fichiers d'une manière similaire à la façon dont le chargeur de classe serait à la recherche de définitions de classe.

Il y a beaucoup d'exemples sur le web pour les fichiers de classe dans la plaine du-vieux-répertoires. La plupart d'entre nous ces jours-travailler avec les fichiers JAR.

Pour obtenir des choses à travailler avec les fichiers JAR, essayez ceci...

private static ArrayList<Class<?>> getClassesForPackage(Package pkg) {
    String pkgname = pkg.getName();
    ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
    // Get a File object for the package
    File directory = null;
    String fullPath;
    String relPath = pkgname.replace('.', '/');
    System.out.println("ClassDiscovery: Package: " + pkgname + " becomes Path:" + relPath);
    URL resource = ClassLoader.getSystemClassLoader().getResource(relPath);
    System.out.println("ClassDiscovery: Resource = " + resource);
    if (resource == null) {
        throw new RuntimeException("No resource for " + relPath);
    }
    fullPath = resource.getFile();
    System.out.println("ClassDiscovery: FullPath = " + resource);

    try {
        directory = new File(resource.toURI());
    } catch (URISyntaxException e) {
        throw new RuntimeException(pkgname + " (" + resource + ") does not appear to be a valid URL / URI.  Strange, since we got it from the system...", e);
    } catch (IllegalArgumentException e) {
        directory = null;
    }
    System.out.println("ClassDiscovery: Directory = " + directory);

    if (directory != null && directory.exists()) {
        // Get the list of the files contained in the package
        String[] files = directory.list();
        for (int i = 0; i < files.length; i++) {
            // we are only interested in .class files
            if (files[i].endsWith(".class")) {
                // removes the .class extension
                String className = pkgname + '.' + files[i].substring(0, files[i].length() - 6);
                System.out.println("ClassDiscovery: className = " + className);
                try {
                    classes.add(Class.forName(className));
                } 
                catch (ClassNotFoundException e) {
                    throw new RuntimeException("ClassNotFoundException loading " + className);
                }
            }
        }
    }
    else {
        try {
            String jarPath = fullPath.replaceFirst("[.]jar[!].*", ".jar").replaceFirst("file:", "");
            JarFile jarFile = new JarFile(jarPath);         
            Enumeration<JarEntry> entries = jarFile.entries();
            while(entries.hasMoreElements()) {
                JarEntry entry = entries.nextElement();
                String entryName = entry.getName();
                if(entryName.startsWith(relPath) && entryName.length() > (relPath.length() + "/".length())) {
                    System.out.println("ClassDiscovery: JarEntry: " + entryName);
                    String className = entryName.replace('/', '.').replace('\\', '.').replace(".class", "");
                    System.out.println("ClassDiscovery: className = " + className);
                    try {
                        classes.add(Class.forName(className));
                    } 
                    catch (ClassNotFoundException e) {
                        throw new RuntimeException("ClassNotFoundException loading " + className);
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(pkgname + " (" + directory + ") does not appear to be a valid package", e);
        }
    }
    return classes;
}

3voto

thvo Points 744

J'ai compris comment faire. Voici la procédure:

  1. Commencer par une classe dans le package racine, et obtenir le dossier c'est à partir de la classe loader
  2. De manière récursive énumérer tous .les fichiers de classe dans ce dossier
  3. Convertir les noms de fichier pleinement qualifié de la classe des noms
  4. Utilisation De La Classe.forName() pour obtenir les classes

Il y a quelques vilains tours ici qui me font un peu mal à l'aise, mais il fonctionne, par exemple:

  1. La conversion des noms de chemin d'accès pour les noms de paquets à l'aide de la manipulation de la chaîne
  2. Coder en dur la racine du nom de package pour permettre d'écarter le préfixe de chemin

Dommage que stackoverflow ne m'autorise pas à accepter ma propre réponse...

2voto

G B Points 66

Je crains que vous n'ayez à scanner manuellement le chemin de classe et les autres endroits où java recherche des classes (par exemple, le répertoire ext ou le chemin de classe de démarrage). Étant donné que java utilise le chargement paresseux des classes, il peut même ne pas connaître les classes supplémentaires de vos packages qui n'ont pas encore été chargées. Vérifiez également la notion de packages «scellés».

2voto

David M. Karr Points 2210

C'est drôle que cette question revienne de temps en temps. Le problème est que ce mot-clé aurait été mieux nommé "namespace". Le package Java ne délimite pas un conteneur concret contenant toutes les classes du package à un moment donné. Il définit simplement un jeton que les classes peuvent utiliser pour déclarer qu'elles sont membres de ce package. Vous devrez rechercher dans tout le chemin de classe (comme une autre réponse l'a indiqué) pour déterminer toutes les classes d'un package.

2voto

Il y a un bémol à cela: ApplicationEngines/conteneurs de servlet comme tomcat et JBoss ont hiérarchique classe chargeurs. L'obtention de la classe de système de chargement ne va pas le faire.

La façon de Tomcat fonctionne (les choses ont peut-être changé, mais mon expérience ne va pas me faire croire le contraire), mais à chaque contexte d'application a son propre chargeur de classe pour que les classes de l'application 'foo' ne pas entrer en collision avec les classes de l'application, fooV2'

Juste à titre d'exemple. Si toutes les classes sont munged dans une classe uber contexte ensuite, vous n'avez aucune idée de si vous avez été en utilisant des classes appropriées pour la version 1 ou version 2.

En outre, chacun doit avoir accès à des classes de système comme java.lang.Chaîne de caractères. C'est la hiérarchie. Il vérifie le contexte local de l'application de la première et la déplace de façon up (c'est ma situation actuelle, en passant).

Pour gérer cela, une meilleure approche serait: cette.getClass().getClassloader()

Dans mon cas, j'ai un webservice qui doit faire l'auto-découverte sur certains modules et il est évident qu'ils résident dans 'cette' webservice cadre ou le système de contexte. En procédant comme ci-dessus-je obtenir pour vérifier à la fois. En recevant seulement le système de chargeur de classe je n'ai pas accès à l'une des classes de l'application (et donc mes ressources sont nuls).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X