Ist es möglich, alle Klassen oder Schnittstellen in einem bestimmten Paket zu finden? (Ein kurzer Blick auf zB Package
scheint es nein zu sein.)
java
reflection
packages
Jonik
quelle
quelle
Antworten:
Aufgrund der Dynamik von Klassenladern ist dies nicht möglich. Klassenlader müssen der VM nicht mitteilen, welche Klassen sie bereitstellen kann, sondern sie erhalten lediglich Anforderungen für Klassen und müssen eine Klasse zurückgeben oder eine Ausnahme auslösen.
Wenn Sie jedoch Ihre eigenen Klassenlader schreiben oder die Klassenpfade und ihre Gläser untersuchen, können Sie diese Informationen finden. Dies erfolgt jedoch über Dateisystemoperationen und nicht über Reflektion. Möglicherweise gibt es sogar Bibliotheken, die Ihnen dabei helfen können.
Wenn Klassen generiert oder remote bereitgestellt werden, können Sie diese Klassen nicht erkennen.
Die normale Methode besteht stattdessen darin, die Klassen, auf die Sie Zugriff benötigen, irgendwo in einer Datei zu registrieren oder sie in einer anderen Klasse zu referenzieren. Oder verwenden Sie einfach die Konvention, wenn es um die Benennung geht.
Nachtrag: In der Reflections Library können Sie Klassen im aktuellen Klassenpfad nachschlagen. Es kann verwendet werden, um alle Klassen in einem Paket zu erhalten:
quelle
Sie sollten sich wahrscheinlich die Open Source Reflections-Bibliothek ansehen . Damit können Sie ganz einfach erreichen, was Sie wollen.
Richten Sie zunächst den Reflexionsindex ein (es ist etwas chaotisch, da die Suche nach allen Klassen standardmäßig deaktiviert ist):
Anschließend können Sie alle Objekte in einem bestimmten Paket abfragen:
quelle
.addUrls(ClasspathHelper.forJavaClassPath())
anstelle des oben genannten hat sie für mich gelöst. Weniger Code auch!Google Guava 14 enthält eine neue Klasse
ClassPath
mit drei Methoden zum Scannen nach Klassen der obersten Ebene:getTopLevelClasses()
getTopLevelClasses(String packageName)
getTopLevelClassesRecursive(String packageName)
Weitere Informationen finden Sie in den
ClassPath
Javadocs .quelle
ClassPath
ist mit markiert@Beta
, so ist möglicherweise keine gute Idee für einige ...Sie können diese Methode 1 verwenden, die das verwendet
ClassLoader
.__________
1 Diese Methode wurde ursprünglich von http://snippets.dzone.com/posts/show/4831 übernommen , das vom Internetarchiv archiviert wurde, wie jetzt verlinkt. Das Snippet ist auch unter https://dzone.com/articles/get-all-classes-within-package verfügbar .
quelle
%20
, aber dernew File()
Konstruktor hat dies als Literal-Prozentzeichen zwei Null behandelt. Ich habe es behoben, indem ich diedirs.add(...)
Zeile wie folgt geändert habe:dirs.add(new File(resource.toURI()));
Dies bedeutete auch, dass ichURISyntaxException
die Throws-Klausel vongetClasses
Frühling
Dieses Beispiel gilt für Spring 4, aber Sie können den Klassenpfad-Scanner auch in früheren Versionen finden.
Google Guava
Hinweis: In Version 14 ist die API weiterhin als @Beta markiert. Achten Sie daher im Produktionscode darauf.
quelle
ClassPath
Klasse in Guava auch gekennzeichnet mit@Beta
: "APIs, die auf Klassen- oder Methodenebene mit der Annotation @Beta gekennzeichnet sind, können sich ändern. Sie können in jeder Hauptversion auf irgendeine Weise geändert oder sogar entfernt werden. Wenn Ihr Code Ist eine Bibliothek selbst (dh sie wird auf dem CLASSPATH von Benutzern außerhalb Ihrer eigenen Kontrolle verwendet), sollten Sie keine Beta-APIs verwenden, es sei denn, Sie verpacken sie neu ... " code.google.com/p/guava-libraries/#Important_WarningsgetAllClasses()
kann die Methode verwendet werden.Hallo. Ich hatte immer einige Probleme mit den oben genannten Lösungen (und auf anderen Websites).
Ich als Entwickler programmiere ein Addon für eine API. Die API verhindert die Verwendung externer Bibliotheken oder Tools von Drittanbietern. Das Setup besteht auch aus einer Mischung aus Code in JAR- oder ZIP-Dateien und Klassendateien, die sich direkt in einigen Verzeichnissen befinden. Mein Code musste also in der Lage sein, um jedes Setup herum zu arbeiten. Nach vielen Recherchen habe ich eine Methode gefunden, die in mindestens 95% aller möglichen Setups funktioniert.
Der folgende Code ist im Grunde die Overkill-Methode, die immer funktioniert.
Der Code:
Dieser Code durchsucht ein bestimmtes Paket nach allen darin enthaltenen Klassen. Es funktioniert nur für alle Klassen in der aktuellen
ClassLoader
.Diese drei Methoden bieten Ihnen die Möglichkeit, alle Klassen in einem bestimmten Paket zu finden.
Sie verwenden es so:
Die Erklärung:
Die Methode erhält zuerst den aktuellen Wert
ClassLoader
. Es ruft dann alle Ressourcen ab, die das Paket enthalten, und iteriert dieseURL
s. Es erstellt dann eineURLConnection
und bestimmt, welche Art von URl wir haben. Es kann entweder ein Verzeichnis (FileURLConnection
) oder ein Verzeichnis in einer JAR- oder ZIP-Datei (JarURLConnection
) sein. Je nachdem, welche Art von Verbindung wir haben, werden zwei verschiedene Methoden aufgerufen.Lassen Sie uns zuerst sehen, was passiert, wenn es ein ist
FileURLConnection
.Zunächst wird geprüft, ob die übergebene Datei vorhanden ist und ein Verzeichnis ist. In diesem Fall wird geprüft, ob es sich um eine Klassendatei handelt. In
Class
diesem Fall wird ein Objekt erstellt und in das Feld eingefügtArrayList
. Wenn es sich nicht um eine Klassendatei handelt, sondern um ein Verzeichnis, iterieren wir einfach hinein und machen dasselbe. Alle anderen Fälle / Dateien werden ignoriert.Wenn dies
URLConnection
eine ist, wirdJarURLConnection
die andere private Hilfsmethode aufgerufen. Diese Methode durchläuft alle Einträge im zip / jar-Archiv. Wenn ein Eintrag eine Klassendatei ist und sich innerhalb des Pakets befindet, wird einClass
Objekt erstellt und im gespeichertArrayList
.Nachdem alle Ressourcen analysiert wurden, gibt es (die Hauptmethode) den
ArrayList
Inhalt aller Klassen im angegebenen Paket zurück, über die der aktuelleClassLoader
Bescheid weiß.Wenn der Prozess zu irgendeinem Zeitpunkt fehlschlägt,
ClassNotFoundException
wird a mit detaillierten Informationen über die genaue Ursache ausgelöst.quelle
sun.net.www.protocol.file.FileURLConnection
, der zur Kompilierungszeit eine Warnung generiert ("Warnung: sun.net.www.protocol.file.FileURLConnection ist eine proprietäre Sun-API und wird möglicherweise in einer zukünftigen Version entfernt"). Gibt es eine Alternative zur Verwendung dieser Klasse oder kann die Warnung mithilfe von Anmerkungen unterdrückt werden?if ( ... instanceof JarURLConnecton) { ... } else { // Asume that the Connection is valid and points to a File }
Es ist das, was ich in meinem Code getan habe, um mit JPA kommentierte Klassen zu durchsuchenOhne zusätzliche Bibliotheken zu verwenden:
quelle
upackage
istnull
... :(String pack = getPackage().getName();
, müssen Sie hinzufügenpack = pack.replaceAll("[.]", "/");
Im Allgemeinen erlauben Klassenlader nicht das Durchsuchen aller Klassen im Klassenpfad. Normalerweise ist der einzige verwendete Klassenladeprogramm UrlClassLoader, aus dem wir die Liste der Verzeichnisse und JAR-Dateien (siehe getURLs ) abrufen und einzeln öffnen können, um die verfügbaren Klassen aufzulisten . Dieser Ansatz, der als Klassenpfad-Scannen bezeichnet wird, ist in Scannotation and Reflections implementiert .
Ein anderer Ansatz besteht darin, die Java Pluggable Annotation Processing API zu verwenden, um einen Annotationsprozessor zu schreiben, der alle kommentierten Klassen zur Kompilierungszeit sammelt und die Indexdatei für die Laufzeit erstellt. Dieser Mechanismus ist in der ClassIndex- Bibliothek implementiert :
Beachten Sie, dass kein zusätzliches Setup erforderlich ist, da das Scannen vollständig automatisiert ist, da der Java-Compiler automatisch alle im Klassenpfad gefundenen Prozessoren erkennt.
quelle
Der robusteste Mechanismus zum Auflisten aller Klassen in einem bestimmten Paket ist derzeit ClassGraph , da er ein möglichst breites Spektrum von Klassenpfadspezifikationsmechanismen verarbeitet , einschließlich des neuen JPMS-Modulsystems. (Ich bin der Autor.)
quelle
Hier ist, wie ich es mache. Ich scanne alle Unterordner (Unterpakete) und versuche nicht, anonyme Klassen zu laden:
quelle
Ich habe ein einfaches Github-Projekt zusammengestellt, das dieses Problem löst:
https://github.com/ddopson/java-class-enumerator
Es sollte sowohl für dateibasierte Klassenpfade als auch für JAR-Dateien funktionieren.
Wenn Sie nach dem Auschecken des Projekts 'make' ausführen, wird Folgendes ausgedruckt:
Siehe auch meine andere Antwort
quelle
Ja, mit wenigen APIs, die Sie können, hier ist, wie ich es gerne mache, mit diesem Problem konfrontiert, das ich im Ruhezustand verwendete und Klassen finden musste, die mit einer bestimmten Anmerkung versehen waren.
Machen Sie diese zu einer benutzerdefinierten Anmerkung, mit der Sie markieren, welche Klassen Sie abholen möchten.
Dann markiere deine Klasse damit wie
Erstellen Sie diese Dienstprogrammklasse mit der folgenden Methode
Rufen Sie die Methode allFoundClassesAnnotatedWithEntityToBeScanned () auf, um einen Satz gefundener Klassen abzurufen .
Sie benötigen die unten angegebenen Bibliotheken
quelle
Sie müssen jeden Klassenladeprogrammeintrag im Klassenpfad nachschlagen:
Wenn der Eintrag ein Verzeichnis ist, suchen Sie einfach im rechten Unterverzeichnis nach:
Wenn der Eintrag die Datei und die JAR-Datei ist, überprüfen Sie die ZIP-Einträge:
Sobald Sie alle Klassennamen im Paket haben, können Sie versuchen, sie mit Reflection zu laden und zu analysieren, ob es sich um Klassen oder Schnittstellen usw. handelt.
quelle
Ich habe versucht, die Reflections-Bibliothek zu verwenden, hatte jedoch einige Probleme bei der Verwendung, und es gab zu viele Jars, die ich einschließen sollte, um einfach die Klassen in einem Paket zu erhalten.
Ich werde eine Lösung veröffentlichen, die ich in dieser doppelten Frage gefunden habe: Wie erhalte ich alle Klassennamen in einem Paket?
Die Antwort wurde von sp00m geschrieben ; Ich habe einige Korrekturen hinzugefügt, damit es funktioniert:
Um es zu verwenden, rufen Sie einfach die in diesem Beispiel erwähnte find-Methode als sp00n auf: Ich habe bei Bedarf die Erstellung von Instanzen der Klassen hinzugefügt.
quelle
Ich habe gerade eine util-Klasse geschrieben, die Testmethoden enthält. Sie können eine Überprüfung durchführen lassen ~
IteratePackageUtil.java:
quelle
Die Lösung von Aleksander Blomskøld hat bei parametrisierten Tests
@RunWith(Parameterized.class)
mit Maven bei mir nicht funktioniert . Die Tests wurden korrekt benannt und auch dort gefunden, aber nicht ausgeführt:Ein ähnliches Problem wurde berichtet hier .
In meinem Fall
@Parameters
Instanzen jeder Klasse in einem Paket erstellt. Die Tests haben gut funktioniert, wenn sie lokal in der IDE ausgeführt wurden. Beim Ausführen von Maven wurden jedoch keine Klassen mit Aleksander Blomskølds Lösung gefunden.Ich habe es mit dem folgenden Ausschnitt zum Laufen gebracht, der von David Pärssons Kommentar zu Aleksander Blomskølds Antwort inspiriert wurde:
quelle
Was ist damit:
Sie können dann die Funktion überladen:
Wenn Sie es testen müssen:
Wenn Ihre IDE keinen Import-Helfer hat:
Es klappt:
von Ihrer IDE
für eine JAR-Datei
ohne externe Abhängigkeiten
quelle
Fast alle Antworten verwenden
Reflections
oder lesen Klassendateien aus dem Dateisystem. Wenn Sie versuchen, Klassen aus dem Dateisystem zu lesen, können Fehler auftreten, wenn Sie Ihre Anwendung als JAR oder eine andere verpacken. Möglicherweise möchten Sie zu diesem Zweck auch keine separate Bibliothek verwenden.Hier ist ein anderer Ansatz, der reines Java ist und nicht vom Dateisystem abhängt.
Java 8 ist kein Muss . Sie können for-Schleifen anstelle von Streams verwenden. Und Sie können es so testen
quelle
ToolProvider.getSystemJavaCompiler()
werden. Dieser Code scannt keine verschachtelten Pakete.Vorausgesetzt, Sie verwenden keine dynamischen Klassenlader, können Sie den Klassenpfad durchsuchen und für jeden Eintrag das Verzeichnis oder die JAR-Datei durchsuchen.
quelle
Erwähnenswert
Wenn Sie eine Liste aller Klassen unter einem Paket haben möchten, können Sie
Reflection
folgendermaßen vorgehen:Dadurch wird eine Liste von Klassen erstellt, die Sie später nach Belieben verwenden können.
quelle
Es ist sehr gut möglich, aber ohne zusätzliche Bibliotheken
Reflections
ist es schwierig ...Es ist schwierig, weil Sie nicht das vollständige Instrument haben, um den Klassennamen zu erhalten.
Und ich nehme den Code meiner
ClassFinder
Klasse:quelle
Basierend auf der Antwort von @ Staale und in dem Versuch, mich nicht auf Bibliotheken von Drittanbietern zu verlassen, würde ich den Dateisystemansatz implementieren, indem ich den physischen Speicherort des ersten Pakets mit folgenden Informationen untersuche :
quelle
Wenn Sie lediglich eine Gruppe verwandter Klassen laden möchten, kann Spring Ihnen helfen.
Spring kann eine Liste oder Karte aller Klassen instanziieren, die eine bestimmte Schnittstelle in einer Codezeile implementieren. Die Liste oder Zuordnung enthält Instanzen aller Klassen, die diese Schnittstelle implementieren.
Als Alternative zum Laden der Liste der Klassen aus dem Dateisystem implementieren Sie stattdessen einfach dieselbe Schnittstelle in allen Klassen, die Sie laden möchten, unabhängig vom Paket, und verwenden Sie Spring, um Ihnen Instanzen aller Klassen bereitzustellen. Auf diese Weise können Sie alle gewünschten Klassen laden (und instanziieren), unabhängig davon, in welchem Paket sie sich befinden.
Wenn Sie jedoch alle in einem Paket haben möchten, lassen Sie einfach alle Klassen in diesem Paket eine bestimmte Schnittstelle implementieren.
quelle
einfaches Java: FindAllClassesUsingPlainJavaReflectionTest.java
PS: Um Boilerplates für die Behandlung von Fehlern usw. zu vereinfachen, verwende ich hier
vavr
undlombok
BibliothekenAndere Implementierungen finden Sie in meinem GitHub-Dolch für Dolchok / Java-Reflection-Find-Annotated-Classes-or-Methods
quelle
Ich konnte keine kurze Arbeit für etwas so Einfaches finden. Also hier ist es, ich habe es selbst gemacht, nachdem ich eine Weile herumgeschraubt habe:
quelle
Wenn Sie im Frühlingsland sind, können Sie verwenden
PathMatchingResourcePatternResolver
;quelle
Dies ist nicht möglich, da möglicherweise nicht alle Klassen im Paket geladen werden, während Sie immer das Paket einer Klasse kennen.
quelle