wake-up-neo.net

Wie lese ich alle Klassen aus einem Java-Paket im Klassenpfad?

Ich muss Klassen lesen, die in einem Java-Paket enthalten sind. Diese Klassen befinden sich im Klassenpfad. Ich muss diese Aufgabe direkt von einem Java-Programm aus erledigen. Kennen Sie einen einfachen Weg?

List<Class> classes = readClassesFrom("my.package")
90
user176803

Wenn Sie Spring in Ihrem Klassenpfad haben, wird das Folgende es tun.

Finden Sie alle Klassen in einem Paket, die mit XmlRootElement kommentiert sind:

private List<Class> findMyTypes(String basePackage) throws IOException, ClassNotFoundException
{
    ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);

    List<Class> candidates = new ArrayList<Class>();
    String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                               resolveBasePackage(basePackage) + "/" + "**/*.class";
    Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
    for (Resource resource : resources) {
        if (resource.isReadable()) {
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
            if (isCandidate(metadataReader)) {
                candidates.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
            }
        }
    }
    return candidates;
}

private String resolveBasePackage(String basePackage) {
    return ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage));
}

private boolean isCandidate(MetadataReader metadataReader) throws ClassNotFoundException
{
    try {
        Class c = Class.forName(metadataReader.getClassMetadata().getClassName());
        if (c.getAnnotation(XmlRootElement.class) != null) {
            return true;
        }
    }
    catch(Throwable e){
    }
    return false;
}
46
Shalom938

Sie können das beschriebene Reflections-Projekt verwenden hier

Es ist ziemlich vollständig und einfach zu bedienen.

Kurze Beschreibung von der obigen Website:

Reflections durchsucht Ihren Klassenpfad, indiziert die Metadaten und ermöglicht es Ihnen, fragt es zur Laufzeit ab und speichert und sammelt diese Informationen möglicherweise für viele Module in Ihrem Projekt.

Beispiel:

Reflections reflections = new Reflections(
    new ConfigurationBuilder()
        .setUrls(ClasspathHelper.forJavaClassPath())
);
Set<Class<?>> types = reflections.getTypesAnnotatedWith(Scannable.class);
29
Renato

Ich verwende dieses, es funktioniert mit Dateien oder Jar-Archiven

public static ArrayList<String>getClassNamesFromPackage(String packageName) throws IOException{
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    URL packageURL;
    ArrayList<String> names = new ArrayList<String>();;

    packageName = packageName.replace(".", "/");
    packageURL = classLoader.getResource(packageName);

    if(packageURL.getProtocol().equals("jar")){
        String jarFileName;
        JarFile jf ;
        Enumeration<JarEntry> jarEntries;
        String entryName;

        // build jar file name, then loop through zipped entries
        jarFileName = URLDecoder.decode(packageURL.getFile(), "UTF-8");
        jarFileName = jarFileName.substring(5,jarFileName.indexOf("!"));
        System.out.println(">"+jarFileName);
        jf = new JarFile(jarFileName);
        jarEntries = jf.entries();
        while(jarEntries.hasMoreElements()){
            entryName = jarEntries.nextElement().getName();
            if(entryName.startsWith(packageName) && entryName.length()>packageName.length()+5){
                entryName = entryName.substring(packageName.length(),entryName.lastIndexOf('.'));
                names.add(entryName);
            }
        }

    // loop through files in classpath
    }else{
    URI uri = new URI(packageURL.toString());
    File folder = new File(uri.getPath());
        // won't work with path which contains blank (%20)
        // File folder = new File(packageURL.getFile()); 
        File[] contenuti = folder.listFiles();
        String entryName;
        for(File actual: contenuti){
            entryName = actual.getName();
            entryName = entryName.substring(0, entryName.lastIndexOf('.'));
            names.add(entryName);
        }
    }
    return names;
}
26
Edoardo Panfili

Spring hat eine ausgezeichnete Klassenpfad-Suchfunktion in der PathMatchingResourcePatternResolver implementiert. Wenn Sie das Präfix classpath*: verwenden, können Sie alle Ressourcen, einschließlich Klassen in einer bestimmten Hierarchie, suchen und sogar filtern, wenn Sie möchten. Dann können Sie die untergeordneten Elemente von AbstractTypeHierarchyTraversingFilter , AnnotationTypeFilter und AssignableTypeFilter verwenden, um diese Ressourcen entweder auf Annotationen auf Klassenebene oder auf von ihnen implementierten Schnittstellen zu filtern.

11
John Ellinwood

Java 1.6.0_24:

public static File[] getPackageContent(String packageName) throws IOException{
    ArrayList<File> list = new ArrayList<File>();
    Enumeration<URL> urls = Thread.currentThread().getContextClassLoader()
                            .getResources(packageName);
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        File dir = new File(url.getFile());
        for (File f : dir.listFiles()) {
            list.add(f);
        }
    }
    return list.toArray(new File[]{});
}

Diese Lösung wurde in der Umgebung EJB getestet.

6
Paul Kuit

Scannotation und Reflections Klassenpfad-Scan-Ansatz verwenden:

Reflections reflections = new Reflections("my.package");
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);

Ein anderer Ansatz ist die Verwendung von Java Pluggable Annotation Processing API , um einen Anmerkungsprozessor zu schreiben, der alle mit Anmerkungen versehenen Klassen zur Kompilierzeit sammelt und die Indexdatei für die Laufzeit erstellt. Dieser Mechanismus ist in ClassIndex library implementiert:

Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");
6
Sławek

Soweit ich weiß, fehlt diese Funktionalität der Java Reflection API noch immer verdächtig. Sie können ein Paketobjekt erhalten, indem Sie Folgendes tun:

Package packageObj = Package.getPackage("my.package");

Aber wie Sie wahrscheinlich bemerkt haben, können Sie die Klassen in diesem Paket nicht auflisten. Zum jetzigen Zeitpunkt müssen Sie sich eher an ein Dateisystem orientieren.

Ich habe einige Implementierungen in this post gefunden 

Ich bin nicht zu 100% sicher, dass diese Methoden funktionieren, wenn Ihre Klassen in JAR-Dateien vergraben sind, aber ich hoffe, dass eine dieser Methoden für Sie geeignet ist.

Ich stimme @skaffman zu. Wenn Sie es anders angehen wollen, empfehle ich das stattdessen.

3

Sie könnten meine Bibliothek FastClasspathScanner ausprobieren. Das Durchsuchen des Klassenpfads ist nicht so einfach wie das Überprüfen der Java.class.path-Eigenschaft und das rekursive Suchen nach Klassen, da der Klassenpfad auf viele Arten angegeben werden kann (z. B. können Sie Class-Path-Einträge zum Jarfile-Manifest hinzufügen). Die Verwendung von Class.forName () initialisiert tatsächlich die Klasse, die möglicherweise nicht Ihren Wünschen entspricht usw. FastClasspathScanner behandelt diese Komplexitäten für Sie.

Führen Sie die folgenden Schritte aus, um alle Klassen in einem Paket aufzulisten:

String packageName = "my.package";
Set<String> classNames = new FastClassPathScanner(packageName)
    .scan()
    .getNamesOfAllClasses();
String packageNamePrefix = packageName + ".";
for (String className : classNames) {
    if (className.startsWith(packageNamePrefix) {
        System.out.println("Found class: " + className);
   }
}

Sie benötigen die Prüfung if (className.startsWith(packageNamePrefix), da eine Klasse, die sich auf eine andere Klasse bezieht (z. B. eine Superklasse) und sich außerhalb des Whitelist-Paketpräfixes my.package befindet, in der von .getNamesOfAllClasses() zurückgegebenen Menge enthalten ist.

3
Luke Hutchison
  1. Bill Burke hat einen (Nice Artikel über Klassenscanning) geschrieben und dann Scannotation.

  2. Hibernate hat dies bereits geschrieben:

    • org.hibernate.ejb.packaging.Scanner
    • org.hibernate.ejb.packaging.NativeScanner
  3. CDI könnte dieses Problem lösen, weiß aber nicht - noch nicht vollständig untersucht 

.

@Inject Instance< MyClass> x;
...
x.iterator() 

Auch für Anmerkungen:

abstract class MyAnnotationQualifier
extends AnnotationLiteral<Entity> implements Entity {}
2
Ondra Žižka

eXtcos sieht vielversprechend aus. Stellen Sie sich vor, Sie möchten alle Klassen finden, die:

  1. Erweitern Sie die Klasse "Component" und speichern Sie sie
  2. Sind mit "MyComponent" und "" kommentiert
  3. Sind im "allgemeinen" Paket. 

Mit eXtcos ist das so einfach wie

ClasspathScanner scanner = new ClasspathScanner();
final Set<Class> classStore = new ArraySet<Class>();

Set<Class> classes = scanner.getClasses(new ClassQuery() {
    protected void query() {
        select().
        from(“common”).
        andStore(thoseExtending(Component.class).into(classStore)).
        returning(allAnnotatedWith(MyComponent.class));
    }
});
2
noelob

Ich habe es zufällig implementiert und es funktioniert in den meisten Fällen. Da es lang ist, lege ich es in eine Datei hier .

Die Idee ist, den Ort der Klassenquelldatei zu finden, die in den meisten Fällen verfügbar ist (eine bekannte Ausnahme sind JVM-Klassendateien - soweit ich sie getestet habe). Wenn sich der Code in einem Verzeichnis befindet, durchsuchen Sie alle Dateien und nur Spot-Klassendateien. Wenn sich der Code in einer JAR-Datei befindet, scannen Sie alle Einträge.

Diese Methode kann nur verwendet werden, wenn:

  1. Sie haben eine Klasse, die sich in demselben Paket befindet, das Sie entdecken möchten. Diese Klasse wird als SeedClass bezeichnet. Wenn Sie beispielsweise alle Klassen in 'Java.io' auflisten möchten, kann die Seed-Klasse Java.io.File sein.

  2. Ihre Klassen befinden sich in einem Verzeichnis oder in einer JAR-Datei, die Quellendaten enthält (nicht Quellcodedatei, sondern nur Quelldatei). Soweit ich es ausprobiert habe, funktioniert es fast zu 100% mit Ausnahme der JVM-Klasse (diese Klassen werden mit der JVM geliefert).

  3. Ihr Programm muss über die Berechtigung verfügen, auf ProtectionDomain dieser Klassen zuzugreifen. Wenn Ihr Programm lokal geladen ist, sollte es kein Problem geben.

Ich habe das Programm nur für meinen normalen Gebrauch getestet, daher kann es immer noch Probleme geben.

Ich hoffe das hilft.

1
NawaMan

Hier ist eine weitere Option, geringfügige Änderung einer anderen Antwort oben/unten:

Reflections reflections = new Reflections("com.example.project.package", 
    new SubTypesScanner(false));
Set<Class<? extends Object>> allClasses = 
    reflections.getSubTypesOf(Object.class);
1
infinity

Das Projekt ldapbeans bietet eine Klasse Scanner , die dies tun.

0
Stephan

verwenden Sie Abhängigkeits-Maven: 

groupId: net.sf.extcos
artifactId: extcos
version: 0.4b

verwenden Sie dann diesen Code: 

ComponentScanner scanner = new ComponentScanner();
        Set classes = scanner.getClasses(new ComponentQuery() {
            @Override
            protected void query() {
                select().from("com.leyton").returning(allExtending(DynamicForm.class));
            }
        });
0
Yalami

Wenn Sie Guava bereits verwenden, können Sie ClassPath verwenden. Schauen Sie sich die Dokumente hier an: https://google.github.io/guava/releases/17.0/api/docs/com/google/common/reflect/ClassPath.html

0
satnam

Damals, als Applets üblich waren, hatte man möglicherweise eine URL im Klassenpfad. Wenn der Classloader eine Klasse benötigt, durchsucht er alle Positionen im Classpath, einschließlich der http-Ressourcen. Da sich im Klassenpfad beispielsweise URLs und Verzeichnisse befinden können, gibt es keine einfache Möglichkeit, eine definitive Liste der Klassen zu erhalten.

Sie können jedoch ziemlich nahe kommen. Einige Frühlingsbibliotheken machen dies jetzt. Sie können alle Gläser auf dem Klassenpfad abrufen und sie wie Dateien öffnen. Sie können diese Dateiliste dann verwenden und eine Datenstruktur erstellen, die Ihre Klassen enthält.

0
brianegge