Question

Disons que j'ai un paquet Java commandes qui contient des classes dont toutes héritent de ICommand puis-je obtenir toutes ces classes en quelque sorte? Je verrouille pour quelque chose parmi les lignes de:

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

Quelque chose comme ça est-il possible?

Était-ce utile?

La solution

Voici un exemple de base, en supposant que les classes ne sont pas emballées dans un fichier JAR:

// 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<*>quot;, "");
    Class<?> cls = Class.forName(packageName + "." + className);
    if (ICommand.class.isAssignableFrom(cls)) {
        commands.add((Class<ICommand>) cls);
    }
}

Autres conseils

Ci-dessous, une implémentation utilisant l'API JSR-199, c'est-à-dire les classes de javax.tools. * :

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

Voici une méthode utilitaire utilisant Spring.

Vous trouverez des détails sur le motif ici

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

Commencez par public Classloader.getResources (nom de chaîne). Demandez au chargeur de classe une classe correspondant à chaque nom du paquet qui vous intéresse. Répétez cette procédure pour tous les chargeurs de classe pertinents.

Oui, mais ce n’est pas la chose la plus facile à faire. Cela pose de nombreux problèmes. Tous les cours ne sont pas faciles à trouver. Certaines classes peuvent se trouver dans: Jar, sous forme de fichier de classe, sur le réseau, etc.

Regardez sur ce fil.

Pour être sûr qu'il s'agisse du type ICommand, vous devez utiliser la réflexion pour rechercher la classe héritante.

Ce serait un outil très utile et JDK devrait nous aider.

Mais c'est probablement mieux fait pendant la construction. Vous savez où se trouvent tous vos fichiers de classe et vous pouvez les inspecter statiquement et créer un graphique. Au moment de l'exécution, vous pouvez interroger ce graphique pour obtenir tous les sous-types. Cela nécessite plus de travail, mais je crois que cela appartient vraiment au processus de construction.

En utilisant la ClasspathSuite de Johannes Link , j'ai pu le faire comme suit:

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

ClasspathClassesFinder recherche les fichiers de classe et les fichiers JAR dans le chemin de classe du système.

Dans votre cas, vous pouvez modifier acceptClass comme suit:

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

Une chose à noter: faites attention à ce que vous retournez dans acceptClassName, car ClasspathClassesFinder fait ensuite pour charger la classe et appeler acceptClass. Si acceptClassName renvoie toujours la valeur true, vous finirez par charger toutes les classes du chemin d'accès aux classes, ce qui peut entraîner une erreur OutOfMemoryError.

Vous pouvez utiliser OpenPojo et procéder ainsi:

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

Ensuite, vous pouvez parcourir la liste et utiliser les fonctionnalités de votre choix.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top