Frage

Hier können sagen, dass ich ein Java-Paket commands haben, die Klassen enthält, die alle aus ICommand erben kann ich irgendwie alle diese Klassen bekommen? Ich Verriegelung für etwas unter den Zeilen:

Package p = Package.getPackage("commands");
Class<ICommand>[] c = p.getAllPackagedClasses(); //not real 

Ist so etwas wie das möglich?

War es hilfreich?

Lösung

Hier ist ein einfaches Beispiel, unter der Annahme, dass Klassen nicht JAR-verpackt:

// Prepare.
String packageName = "com.example.commands";
List<Class<ICommand>> commands = new ArrayList<Class<ICommand>>();
URL root = Thread.currentThread().getContextClassLoader().getResource(packageName.replace(".", "/"));

// Filter .class files.
File[] files = new File(root.getFile()).listFiles(new FilenameFilter() {
    public boolean accept(File dir, String name) {
        return name.endsWith(".class");
    }
});

// Find classes implementing ICommand.
for (File file : files) {
    String className = file.getName().replaceAll(".class$", "");
    Class<?> cls = Class.forName(packageName + "." + className);
    if (ICommand.class.isAssignableFrom(cls)) {
        commands.add((Class<ICommand>) cls);
    }
}

Andere Tipps

Im Folgenden eine Implementierung des JSR-199 API heißen Klassen von javax.tools.* mit:

List<Class> commands = new ArrayList<Class>();

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(
        null, null, null);

Location location = StandardLocation.CLASS_PATH;
String packageName = "commands";
Set<JavaFileObject.Kind> kinds = new HashSet<JavaFileObject.Kind>();
kinds.add(JavaFileObject.Kind.CLASS);
boolean recurse = false;

Iterable<JavaFileObject> list = fileManager.list(location, packageName,
        kinds, recurse);

for (JavaFileObject javaFileObject : list) {
    commands.add(javaFileObject.getClass());
}

Hier ist eine Dienstprogramm Methode, Frühling verwenden.

Details über das Muster kann hier

    public static List<Class> listMatchingClasses(String matchPattern) throws IOException {
    List<Class> classes = new LinkedList<Class>();
    PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver();
    Resource[] resources = scanner.getResources(matchPattern);

    for (Resource resource : resources) {
        Class<?> clazz = getClassFromResource(resource);
        classes.add(clazz);
    }

    return classes;
}



public static Class getClassFromResource(Resource resource) {
    try {
        String resourceUri = resource.getURI().toString();
        resourceUri = resourceUri.replace(esourceUri.indexOf(".class"), "").replace("/", ".");
        // try printing the resourceUri before calling forName, to see if it is OK.
        return Class.forName(resourceUri);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

Starten Sie mit öffentlichen Classloader.getResources (String name). Fragen Sie den Classloader für eine Klasse zu jedem Namen Interesse an dem Paket, das Sie sind entsprechend. Wiederholen Sie dies für alle Klassenladeprogramme von Bedeutung.

Ja, aber es ist nicht die einfachste Sache zu tun. Es gibt viele Probleme mit diesem. Nicht alle Klassen sind leicht zu finden. Einige Klassen in einem sein könnte. Jar, als Klassendatei über das Netzwerk etc

Werfen Sie einen Blick auf diesen Thread.

Um sicherzustellen, dass sie der ICommand Typ dann müßten Sie Reflexion verwenden für die erbenden Klasse zu überprüfen.

Dies wäre ein sehr nützliches Werkzeug, das wir brauchen, und JDK sollte eine gewisse Unterstützung bieten.

Aber es ist wahrscheinlich besser, während Build getan. Sie wissen, wo Sie alle Ihre Klassendateien sind und Sie können sie statisch überprüfen und einen Graphen aufzubauen. Zur Laufzeit können Sie diese Grafik Abfrage alle Subtypen zu erhalten. Dies erfordert mehr Arbeit, aber ich glaube, es gehört wirklich zu dem Build-Prozess.

Mit Johannes Link zur ClasspathSuite , konnte ich es, wie dies tun:

import org.junit.extensions.cpsuite.ClassTester;
import org.junit.extensions.cpsuite.ClasspathClassesFinder;

public static List<Class<?>> getClasses(final Package pkg, final boolean includeChildPackages) {
    return new ClasspathClassesFinder(new ClassTester() {
        @Override public boolean searchInJars() { return true; }
        @Override public boolean acceptInnerClass() { return false; }
        @Override public boolean acceptClassName(String name) {
            return name.startsWith(pkg.getName()) && (includeChildPackages || name.indexOf(".", pkg.getName().length()) != -1);
        }
        @Override public boolean acceptClass(Class<?> c) { return true; }
    }, System.getProperty("java.class.path")).find();
}

Die ClasspathClassesFinder sucht nach Klassendateien und Gläser im System Classpath.

In Ihrem speziellen Fall, Sie acceptClass wie dies ändern könnten:

@Override public boolean acceptClass(Class<?> c) {
    return ICommand.class.isAssignableFrom(c);
}

Eine Sache zu beachten: Seien Sie vorsichtig, was Sie in acceptClassName zurückkehren, wie das nächste, was ClasspathClassesFinder tut, ist die Klasse und rufen acceptClass zu laden. Wenn acceptClassName immer true zurück, werden Sie jede Klasse in den Classpath Laden am Ende und kann eine OutOfMemoryError führen.

Sie könnten verwenden OpenPojo und dies tun:

final List<PojoClass> pojoClasses = PojoClassFactory.getPojoClassesRecursively("my.package.path", null);

Dann können Sie die Liste durchgehen und führen Sie alle Funktionen, die Sie sich wünschen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top