643 Stimmen

Können Sie mit Hilfe von Reflection alle Klassen in einem Paket finden?

Ist es möglich, alle Klassen oder Schnittstellen in einem bestimmten Paket zu finden? (Ein schneller Blick auf z.B. Package scheint das nicht der Fall zu sein.)

2 Stimmen

FYI die Lösung Amit Links zu funktioniert, obwohl es einen Fehler hat, wenn der Klassenpfad ein Leerzeichen in es hat (und wahrscheinlich für andere nicht-alphanumerische Zeichen zu). wenn Sie es in jeder Art von Produktionscode verwenden, siehe mein Kommentar zu seiner Antwort für eine Problemumgehung.

2 Stimmen

Beachten Sie auch diese Stelle .

1 Stimmen

Siehe dazugehörige Antwort: stackoverflow.com/a/30149061/4102160

419voto

Staale Punkte 25764

Aufgrund der dynamischen Natur von Klassenladern ist dies nicht möglich. Klassenlader sind nicht verpflichtet, der VM mitzuteilen, welche Klassen sie bereitstellen können. Stattdessen werden ihnen lediglich Anforderungen für Klassen übergeben, und sie müssen eine Klasse zurückgeben oder eine Ausnahme auslösen.

Wenn Sie jedoch Ihre eigenen Klassenlader schreiben oder die Klassenpfade und die Jars untersuchen, ist es möglich, diese Informationen zu finden. Dies geschieht jedoch über Dateisystemoperationen und nicht über Reflexion. Vielleicht gibt es sogar Bibliotheken, die Ihnen dabei helfen können.

Wenn es Klassen gibt, die aus der Ferne erzeugt oder geliefert werden, können Sie diese Klassen nicht erkennen.

Die normale Methode ist stattdessen, 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 Konventionen, wenn es um die Namensgebung geht.

Nachtrag: Die Bibliothek der Reflexionen ermöglicht es Ihnen, im aktuellen Klassenpfad nach Klassen zu suchen. Es kann verwendet werden, um alle Klassen in einem Paket zu erhalten:

 Reflections reflections = new Reflections("my.project.prefix");

 Set<Class<? extends Object>> allClasses = 
     reflections.getSubTypesOf(Object.class);

14 Stimmen

Die Unmöglichkeit, nach Klassennamen zu suchen, hat mich schon lange gestört. Sicher, es ist schwierig und die Leistung kann stark variieren, und für bestimmte Classloader ist die Liste undefiniert oder unbegrenzt, aber es gibt Möglichkeiten, dies zu umgehen.

22 Stimmen

Beachten Sie, dass diese Lösung nicht funktionieren wird, da getSubTypesOf standardmäßig keine Subtypen von Object zurückgibt. Siehe Aleksander Blomskølds Lösung für die Konfiguration des SubTypeScanners.

20 Stimmen

Reflections erfordert Guava. Guava ist groß. Version 14.0.1 ist 2,1MB groß.

209voto

Vielleicht sollten Sie sich die Open-Source-Software Bibliothek der Reflexionen . Mit ihm können Sie leicht erreichen, was Sie wollen.

Richten Sie zunächst den Reflexionsindex ein (er ist ein wenig unübersichtlich, da die Suche nach allen Klassen standardmäßig deaktiviert ist):

List<ClassLoader> classLoadersList = new LinkedList<ClassLoader>();
classLoadersList.add(ClasspathHelper.contextClassLoader());
classLoadersList.add(ClasspathHelper.staticClassLoader());

Reflections reflections = new Reflections(new ConfigurationBuilder()
    .setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
    .setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])))
    .filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("org.your.package"))));

Dann können Sie nach allen Objekten in einem bestimmten Paket suchen:

Set<Class<?>> classes = reflections.getSubTypesOf(Object.class);

0 Stimmen

Nur zur Erinnerung - der obige Quellcode funktioniert bei mir unter reflections-0.9.8 nicht mehr. Die Verwendung spezifischerer Typen funktioniert, aber Object.class funktioniert nicht. Ich habe noch keine Lösung für dieses Problem gefunden; es ist möglich, dass es spezifisch für mich ist, aber ich kann noch keine Ursache erkennen.

6 Stimmen

Ah, jetzt geht's los: code.google.com/p/reflections/issues/detail?id=122 . Objekt ist standardmäßig ausgeschlossen, aber Sie können es umstellen. Danke für den Hinweis auf diese Bibliothek, sie ist großartig!

1 Stimmen

Ich hatte auf meinem Mac Probleme mit diesem Code (im Zusammenhang mit nativen Bibliotheken), aber mit .addUrls(ClasspathHelper.forJavaClassPath()) anstelle der obigen Lösung für mich. Und auch weniger Code!

151voto

Sie könnten verwenden diese Methode 1 die den ClassLoader .

/**
 * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
 *
 * @param packageName The base package
 * @return The classes
 * @throws ClassNotFoundException
 * @throws IOException
 */
private static Class[] getClasses(String packageName)
        throws ClassNotFoundException, IOException {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    assert classLoader != null;
    String path = packageName.replace('.', '/');
    Enumeration<URL> resources = classLoader.getResources(path);
    List<File> dirs = new ArrayList<File>();
    while (resources.hasMoreElements()) {
        URL resource = resources.nextElement();
        dirs.add(new File(resource.getFile()));
    }
    ArrayList<Class> classes = new ArrayList<Class>();
    for (File directory : dirs) {
        classes.addAll(findClasses(directory, packageName));
    }
    return classes.toArray(new Class[classes.size()]);
}

/**
 * Recursive method used to find all classes in a given directory and subdirs.
 *
 * @param directory   The base directory
 * @param packageName The package name for classes found inside the base directory
 * @return The classes
 * @throws ClassNotFoundException
 */
private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
    List<Class> classes = new ArrayList<Class>();
    if (!directory.exists()) {
        return classes;
    }
    File[] files = directory.listFiles();
    for (File file : files) {
        if (file.isDirectory()) {
            assert !file.getName().contains(".");
            classes.addAll(findClasses(file, packageName + "." + file.getName()));
        } else if (file.getName().endsWith(".class")) {
            classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
        }
    }
    return classes;
}

__________
<sup>1 </sup>Diese Methode wurde ursprünglich übernommen von <a href="http://snippets.dzone.com/posts/show/4831" rel="noreferrer">http://snippets.dzone.com/posts/show/4831 </a>das war <a href="https://web.archive.org/web/20090227113602/http://snippets.dzone.com:80/posts/show/4831" rel="noreferrer">archiviert </a>durch das Internet Archive, wie jetzt verlinkt. Das Snippet ist auch verfügbar unter <a href="https://dzone.com/articles/get-all-classes-within-package" rel="noreferrer">https://dzone.com/articles/get-all-classes-within-package </a>.

9 Stimmen

Ich hatte ein Problem damit, wenn mein Pfad Leerzeichen enthielt. Die URL-Klasse hat die Leerzeichen in %20 aber die new File() Konstruktor behandelte das als ein buchstäbliches Prozentzeichen mit zwei Nullen. Ich habe es behoben, indem ich die dirs.add(...) Linie dazu: dirs.add(new File(resource.toURI())); Dies bedeutete auch, dass ich Folgendes hinzufügen musste URISyntaxException in die throws-Klausel von getClasses

24 Stimmen

Sie haben gerade kopiert von dzone.com/articles/get-all-classes-within-package ! beim nächsten Mal bitte Quelle angeben

27 Stimmen

+1 weil diese Lösung KEINE externen Bibliotheken benötigt... NIEMALS, wirklich NIEMALS koppeln Sie Ihren Code wahllos mit Bibliotheken, nur um eine kleine Sache wie diese zu erreichen. Nov 2015 wurde ein Apache Commons-Problem entdeckt, das zu Remote Command Execution führt, nur weil Apache Commons im Klassenpfad einer auf Jboss/Weblogic deployten App liegt [ foxglovesecurity.com/2015/11/06/

145voto

Christoph Leiter Punkte 8807

Google Guava 14 enthält eine neue Klasse ClassPath mit drei Methoden, um nach Klassen der obersten Ebene zu suchen:

  • getTopLevelClasses()
  • getTopLevelClasses(String packageName)
  • getTopLevelClassesRecursive(String packageName)

Siehe die ClassPath javadocs für weitere Informationen.

0 Stimmen

Dies funktionierte für mich, wo Reflection nicht tun konnte (kein gemeinsamer direkter Vorfahre, keine gemeinsame Annotation)

1 Stimmen

Wie ich bereits in ein Kommentar unten , ClassPath ist getaggt mit @Beta ...also vielleicht keine gute Idee für einige...

1 Stimmen

Zu sagen, dass dies funktioniert, wo Reflection nicht tut, ist ein bisschen seltsam, die Lösung zweifellos mit Reflection (und Class Loader) Funktionalität implementiert wird.

127voto

voho Punkte 2537

Frühling

Dieses Beispiel ist für Spring 4, aber Sie können den Klassenpfad-Scanner auch in früheren Versionen finden.

// create scanner and disable default filters (that is the 'false' argument)
final ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
// add include filters which matches all the classes (or use your own)
provider.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*")));

// get matching classes defined in the package
final Set<BeanDefinition> classes = provider.findCandidateComponents("my.package.name");

// this is how you can load the class type from BeanDefinition instance
for (BeanDefinition bean: classes) {
    Class<?> clazz = Class.forName(bean.getBeanClassName());
    // ... do your magic with the class ...
}

Google Guava

Note : In Version 14 ist die API noch als @Beta also Vorsicht im Produktionscode.

final ClassLoader loader = Thread.currentThread().getContextClassLoader();

for (final ClassPath.ClassInfo info : ClassPath.from(loader).getTopLevelClasses()) {
  if (info.getName().startsWith("my.package.")) {
    final Class<?> clazz = info.load();
    // do something with your clazz
  }
}

8 Stimmen

Ausgezeichnete Antwort. Es gibt hier zu viele Lösungen, die langatmig sind, nicht getestet wurden und nicht funktionieren! Diese ist fantastisch: Sie ist knapp und getestet (sie ist von Guava). Sehr gut! Sie ist nützlich und verdient mehr Upvotes.

0 Stimmen

Leider ist die ClassPath Klasse in Guava ist ebenfalls mit @Beta : "APIs, die mit der @Beta-Annotation auf Klassen- oder Methodenebene gekennzeichnet sind, unterliegen Änderungen. Sie können in jeder größeren Version in irgendeiner Weise geändert oder sogar entfernt werden. Wenn Ihr Code selbst eine Bibliothek ist (d.h. er wird im CLASSPATH von Benutzern außerhalb Ihrer eigenen Kontrolle verwendet), sollten Sie keine Beta-APIs verwenden, es sei denn, Sie packen sie neu..." code.google.com/p/guava-libraries/#Wichtige_Warnungen

0 Stimmen

@Christian Guter Punkt, habe ich nicht bemerkt! Vielen Dank dafür. Ich werde eine weitere Antwort mit Spring Klassenpfad-Scanner hinzufügen, die nicht sicher Beta ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X