it-swarm.com.de

Finden Sie Java Klassen, die eine Schnittstelle implementieren

Vor einiger Zeit stieß ich auf einen Code, der einige Standardfunktionen von Java verwendete, um die Klassen zu lokalisieren, die eine bestimmte Schnittstelle implementierten. Ich weiß, dass die Funktionen an einem nicht logischen Ort versteckt waren, aber sie könnten für andere Klassen verwendet werden, wie der Paketname impliziert. Damals habe ich es nicht gebraucht, also habe ich es vergessen, aber jetzt finde ich die Funktionen nicht wieder. Wo sind diese Funktionen zu finden?

Bearbeiten: Ich suche keine IDE Funktionen oder etwas anderes, sondern etwas, das in der Java Anwendung ausgeführt werden kann.

121
Linor

Vor einiger Zeit habe ich ein Paket zusammengestellt, um zu tun, was Sie wollen, und vieles mehr. (Ich brauchte es für ein Dienstprogramm, das ich schrieb). Es wird die Bibliothek ASM verwendet. Sie können Reflection verwenden, aber ASM hat sich als leistungsstärker erwiesen.

Ich stelle mein Paket in eine Open-Source-Bibliothek, die ich auf meiner Website habe. Die Bibliothek ist hier: http://software.clapper.org/javautil/ . Sie möchten mit der Klasse ClassFinder beginnen.

Das Dienstprogramm, für das ich es geschrieben habe, ist ein RSS-Reader, den ich immer noch jeden Tag benutze, sodass der Code in der Regel nicht ausgeführt wird. Ich verwende ClassFinder, um eine Plug-in-API im RSS-Reader zu unterstützen. Beim Start wird in einigen Verzeichnisbäumen nach JARS und Klassendateien gesucht, die Klassen enthalten, die eine bestimmte Schnittstelle implementieren. Es ist viel schneller als Sie vielleicht erwarten.

Die Bibliothek ist BSD-lizenziert, sodass Sie sie sicher mit Ihrem Code bündeln können. Quelle ist verfügbar.

Wenn dir das nützlich ist, hilf dir selbst.

Update: Wenn Sie Scala verwenden, ist diese Bibliothek möglicherweise besser für Scala geeignet.

63
Brian Clapper

Der Frühling kann das für Sie tun ...

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. Der TypeFilter ist wichtig, wenn Sie die richtigen Ergebnisse erzielen möchten! Sie können stattdessen auch Ausschlussfilter verwenden.

Der Scanner befindet sich im Spring-Context-Jar, die Registry in Spring-Beans, der Type-Filter im Spring-Core.

50
Nick Robson

Ich mag die Reflections Library sehr dafür.

Es bietet viele verschiedene Arten von Scannern (getTypesAnnotatedWith, getSubTypesOf usw.), und es ist kinderleicht, eigene zu schreiben oder zu erweitern.

29
robertvoliva

Der Code, von dem Sie sprechen, klingt wie ServiceLoader, , der in Java 6 eingeführt wurde, um eine Funktion zu unterstützen, die seit definiert wurde. Java 1. oder früher Aus Performancegründen ist dies der empfohlene Ansatz, um Schnittstellenimplementierungen zur Laufzeit zu finden. Wenn Sie in einer älteren Java-Version Unterstützung dafür benötigen, hoffe ich, dass Sie meine Implementierung) finden hilfreich.

Es gibt einige Implementierungen in früheren Versionen von Java, aber in den Sun-Paketen, nicht in der Kern-API (ich glaube, es gibt einige Klassen innerhalb von ImageIO, die dies tun). Da der Code einfach ist, empfehle ich, Ihre eigene Implementierung bereitzustellen, anstatt sich auf nicht standardmäßigen Sun-Code zu verlassen, der Änderungen unterworfen ist.

22
erickson

Anmerkungen auf Paketebene

Ich weiß, dass diese Frage bereits vor langer Zeit beantwortet wurde, aber eine andere Lösung für dieses Problem ist die Verwendung von Annotationen auf Paketebene.

Während es ziemlich schwierig ist, alle Klassen in der JVM zu finden, ist es tatsächlich ziemlich einfach, die Pakethierarchie zu durchsuchen.

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

Dann machen Sie einfach Ihre Annotation mit einem Argument eines Arrays von Class. Tragen Sie dann in Ihre package-info.Java für ein bestimmtes Paket den MyAno ein.

Ich werde weitere Details (Code) hinzufügen, wenn die Leute interessiert sind, aber höchstwahrscheinlich auf die Idee kommen.

MetaInf Service Loader

Zum Hinzufügen zu @erickson answer können Sie auch den Service Loader-Ansatz verwenden. Kohsuke hat eine großartige Möglichkeit, das erforderliche META-INF-Zeug zu generieren, das Sie für den Service Loader-Ansatz benötigen:

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

13
Adam Gent

Sie können auch den Extensible Component Scanner (extcos: http://sf.net/projects/extcos ) verwenden und alle Klassen durchsuchen, die eine Schnittstelle wie folgt implementieren:

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());
    }
});

Dies funktioniert für Klassen im Dateisystem, innerhalb von JARS und sogar für Klassen im virtuellen JBoss-Dateisystem. Es ist außerdem für die Verwendung in eigenständigen Anwendungen sowie in jedem Web- oder Anwendungscontainer konzipiert.

8
Matthias Rothe

Im Allgemeinen ist diese Funktionalität nicht möglich. Der Java ClassLoader-Mechanismus garantiert nur die Fähigkeit, nach einer Klasse mit einem bestimmten Namen (einschließlich pacakge) zu fragen, und der ClassLoader kann eine Klasse bereitstellen oder angeben, dass er diese Klasse nicht kennt .

Klassen können (und werden häufig) von Remote-Servern geladen, und sie können sogar im laufenden Betrieb erstellt werden. Es ist überhaupt nicht schwierig, einen ClassLoader zu schreiben, der eine gültige Klasse zurückgibt, die eine bestimmte Schnittstelle für any name implementiert, den Sie von ihr verlangen. Eine Liste der Klassen, die diese Schnittstelle implementieren, wäre dann unendlich lang.

In der Praxis ist der häufigste Fall ein URLClassLoader, der nach Klassen in einer Liste von Dateisystemverzeichnissen und JAR-Dateien sucht. Sie müssen also das URLClassLoader abrufen, diese Verzeichnisse und Archive durchlaufen und für jede darin enthaltene Klassendatei das entsprechende Class -Objekt anfordern und die Rückgabe des zugehörigen Objekts überprüfen getInterfaces() Methode.

6

Offensichtlich gibt Class.isAssignableFrom () an, ob eine individual - Klasse die angegebene Schnittstelle implementiert. Das Problem besteht also darin, die Liste der zu testenden Klassen abzurufen.

Soweit mir bekannt ist, gibt es von Java) keine direkte Möglichkeit, den Klassenlader nach "der Liste der Klassen zu fragen, die Sie möglicherweise laden könnten". Sie müssen dies also selbst tun, indem Sie die sichtbaren Jars durchlaufen, Class.forName () aufrufen, um die Klasse zu laden, und sie dann testen.

Es ist jedoch etwas einfacher, wenn Sie nur die Klassen kennen möchten, die die angegebene Schnittstelle implementieren, von denen, die tatsächlich geladen wurden:

  • über das Framework Java Instrumentation können Sie Instrumentation.getAllLoadedClasses () aufrufen.
  • über Reflection können Sie das ClassLoader.classes -Feld eines bestimmten ClassLoader abfragen.

Wenn Sie die Instrumentationstechnik verwenden, wird Ihre "Agent" -Klasse (wie im Link erläutert) im Wesentlichen beim Start der JVM aufgerufen und hat ein Instrumentation-Objekt übergeben. Zu diesem Zeitpunkt möchten Sie es wahrscheinlich in einem statischen Feld "für später speichern" und später von Ihrem Hauptanwendungscode aufrufen lassen, um die Liste der geladenen Klassen abzurufen.

5
Neil Coffey

Wenn Sie aus der Perspektive der Arbeit mit einem laufenden Programm gefragt haben, müssen Sie sich das Java.lang. * -Paket ansehen. Wenn Sie ein Class-Objekt erhalten, können Sie mit der Methode isAssignableFrom prüfen, ob es sich um eine Schnittstelle einer anderen Klasse handelt.

Es gibt keine einfache Methode, nach diesen zu suchen. Tools wie Eclipse erstellen einen Index dieser Informationen.

Wenn Sie keine bestimmte Liste zu testender Klassenobjekte haben, können Sie das ClassLoader-Objekt überprüfen, die getPackages () -Methode verwenden und Ihren eigenen Iterator für die Pakethierarchie erstellen.

Nur eine Warnung, dass diese Methoden und Klassen sehr langsam sein können.

0
Matt Large