web-dev-qa-db-fra.com

Trouver les classes Java implémentant une interface

Il y a quelque temps, je suis tombé sur un morceau de code, qui utilisait un élément de la fonctionnalité standard Java pour localiser les classes qui implémentaient une interface donnée. Je sais que les fonctions étaient cachées dans une structure non logique. Dans ce cas, je n’en avais pas besoin, j’en ai oublié le mot, mais je le fais maintenant et j’ai l'impression de ne pas pouvoir retrouver les fonctions. fonctions être trouvé?

Edit: Je ne cherche aucune fonction IDE ou autre chose, mais quelque chose qui peut être exécuté dans l’application Java.

121
Linor

Il y a quelque temps, j'ai mis au point un package pour faire ce que vous voulez et plus encore. (J'en avais besoin pour un utilitaire que j'écrivais). Il utilise la bibliothèque ASM . Vous pouvez utiliser la réflexion, mais ASM s’est avéré mieux performer.

Je mets mon paquet dans une bibliothèque open source que j'ai sur mon site web. La bibliothèque est ici: http://software.clapper.org/javautil/ . Vous voulez commencer avec la classe with ClassFinder .

L'utilitaire pour lequel je l'ai écrit est un lecteur RSS que j'utilise toujours tous les jours, de sorte que le code a tendance à s'exercer. J'utilise ClassFinder pour prendre en charge une API de plug-in dans le lecteur RSS. au démarrage, il recherche dans quelques arborescences de répertoires des fichiers JAR et des fichiers de classe contenant des classes qui implémentent une interface donnée. C'est beaucoup plus rapide que vous ne le pensez.

La bibliothèque est sous licence BSD, vous pouvez donc la regrouper en toute sécurité avec votre code. La source est disponible.

Si cela vous est utile, aidez-vous.

Mise à jour: Si vous utilisez Scala, vous pourriez trouver cette bibliothèque plus compatible avec Scala.

63
Brian Clapper

Le printemps peut le faire pour vous ...

BeanDefinitionRegistry bdr = new SimpleBeanDefinitionRegistry();
ClassPathBeanDefinitionScanner s = new ClassPathBeanDefinitionScanner(bdr);

TypeFilter tf = new AssignableTypeFilter(CLASS_YOU_WANT.class);
s.addIncludeFilter(tf);
s.scan("package.you.want1", "package.you.want2");       
String[] beans = bdr.getBeanDefinitionNames();

N.B. Le TypeFilter est important si vous voulez les bons résultats! Vous pouvez également utiliser des filtres d'exclusion ici.

Le scanner peut être trouvé dans le bocal contenant le printemps, le registre dans les haricots de printemps, le filtre type est dans le noyau de printemps.

50
Nick Robson

J'aime beaucoup le bibliothèque de réflexions pour le faire.

Il fournit beaucoup de types de scanners différents (getTypesAnnotatedWith, getSubTypesOf, etc.), et il est extrêmement simple d'écrire ou d'étendre le vôtre.

29
robertvoliva

Le code dont vous parlez ressemble à ServiceLoader, qui a été introduit dans Java 6 pour prendre en charge une fonctionnalité définie depuis Java 1. ou antérieure Pour des raisons de performances, cette approche est recommandée pour rechercher les implémentations d'interface au moment de l'exécution; si vous avez besoin de cela dans une version plus ancienne de Java, j'espère que vous trouverez mon implémentation utile.

Il existe quelques implémentations de cela dans les versions antérieures de Java, mais dans les packages Sun, pas dans l'API principale (je pense qu'il en est ainsi dans certaines classes internes à ImageIO). Comme le code est simple, je vous recommande de fournir votre propre implémentation plutôt que de vous fier à du code Sun non standard, sujet à modification.

22
erickson

Annotations au niveau du package

Je sais que cette question a déjà reçu une réponse il y a longtemps, mais une autre solution à ce problème consiste à utiliser les annotations au niveau du package.

Bien qu'il soit difficile de trouver toutes les classes de la machine virtuelle, il est assez facile de parcourir la hiérarchie des paquets.

Package[] ps = Package.getPackages();
for (Package p : ps) {
  MyAno a = p.getAnnotation(MyAno.class)
  // Recursively descend
}

Ensuite, il suffit que votre annotation ait un argument d'un tableau de classe. Ensuite, dans votre paquet-info.Java pour un paquet particulier, mettez le MyAno.

J'ajouterai plus de détails (code) si les gens sont intéressés mais ont très probablement compris l'idée.

MetaInf Service Loader

Pour ajouter une réponse à @erickson, vous pouvez également utiliser l'approche du chargeur de service. Kohsuke dispose d'un excellent moyen de générer les éléments META-INF requis pour l'approche Service Loader:

http://weblogs.Java.net/blog/kohsuke/archive/2009/03/my_project_of_t.html

13
Adam Gent

Vous pouvez également utiliser l'analyseur de composants extensible (extcos: http://sf.net/projects/extcos ) et rechercher toutes les classes implémentant une interface comme ceci:

Set<Class<? extends MyInterface>> classes = new HashSet<Class<? extends MyInterface>>();

ComponentScanner scanner = new ComponentScanner();
scanner.getClasses(new ComponentQuery() {
    @Override
    protected void query() {
        select().
        from("my.package1", "my.package2").
        andStore(thoseImplementing(MyInterface.class).into(classes)).
        returning(none());
    }
});

Cela fonctionne pour les classes du système de fichiers, dans les fichiers JAR et même pour celles du système de fichiers virtuel JBoss. Il est en outre conçu pour fonctionner dans les applications autonomes ainsi que dans tout conteneur Web ou d'application.

8
Matthias Rothe

En toute généralité, cette fonctionnalité est impossible. Le mécanisme Java ClassLoader garantit uniquement la possibilité de demander une classe portant un nom spécifique (y compris pacakge), et le ClassLoader peut fournir une classe ou indiquer qu'il ne connaît pas cette classe. .

Les classes peuvent être (et sont fréquemment) chargées à partir de serveurs distants et peuvent même être construites à la volée; il n'est pas difficile du tout d'écrire un ClassLoader qui retourne une classe valide qui implémente une interface donnée pour tout le nom que vous lui demandez; Une liste des classes qui implémentent cette interface aurait alors une longueur infinie.

En pratique, le cas le plus courant est un URLClassLoader qui recherche des classes dans une liste de répertoires de système de fichiers et de fichiers JAR. Donc, ce dont vous avez besoin, c’est d’obtenir le URLClassLoader, puis de le parcourir dans ces répertoires et archives, et pour chaque fichier de classe qu’il contient, demandez à l’objet Class correspondant et regardez dans le retour de son répertoire. getInterfaces() méthode.

6
Michael Borgwardt

De toute évidence, Class.isAssignableFrom () vous indique si une classe individual implémente l'interface donnée. Le problème consiste donc à obtenir la liste des classes à tester.

Autant que je sache, il n'y a pas de moyen direct depuis Java de demander au chargeur de classes "la liste des classes que vous pourriez potentiellement charger". Vous devrez donc le faire vous-même en effectuant une itération dans les fichiers JAR visibles, en appelant Class.forName () pour charger la classe, puis en la testant.

Cependant, c'est un peu plus facile si vous voulez juste connaître les classes implémentant l'interface donnée à partir de celles qui ont en fait chargées:

  • via le Java Instrumentation framework, vous pouvez appeler Instrumentation.getAllLoadedClasses () ()
  • via réflexion, vous pouvez interroger le champ ClassLoader.classes d'un ClassLoader donné.

Si vous utilisez la technique d'instrumentation, alors (comme expliqué dans le lien), votre classe "agent" est appelée essentiellement au démarrage de la machine virtuelle Java et a transmis un objet Instrumentation. À ce stade, vous souhaiterez probablement "le sauvegarder pour plus tard" dans un champ statique, puis demander à votre code d'application principal de l'appeler ultérieurement pour obtenir la liste des classes chargées.

5
Neil Coffey

Si vous demandez comment travailler avec un programme en cours, vous devez vous tourner vers le package Java.lang. *. Si vous obtenez un objet Class, vous pouvez utiliser la méthode isAssignableFrom pour vérifier s'il s'agit d'une interface d'une autre classe.

Il n’existe pas de méthode simple de recherche, des outils comme Eclipse construisent un index de ces informations.

Si vous ne disposez pas d'une liste spécifique d'objets de classe à tester, vous pouvez vous reporter à l'objet ClassLoader, utilisez la méthode getPackages () et créez votre propre itérateur de hiérarchie de packages.

Juste un avertissement cependant que ces méthodes et classes peuvent être assez lentes.

0
Matt Large