Domanda

Diciamo che ho un pacchetto java comandi che contiene classi che tutti ereditano da ICommand posso ottenere tutte quelle classi in qualche modo? Sto cercando qualcosa tra le righe di:

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

È possibile qualcosa del genere?

È stato utile?

Soluzione

Ecco un esempio di base, supponendo che le classi non siano pacchetti 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);
    }
}

Altri suggerimenti

Di seguito, un'implementazione che utilizza l'API JSR-199, ovvero le classi da 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());
}

Ecco un metodo di utilità, usando Spring.

Dettagli sul modello sono disponibili qui

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

Inizia con Classloader.getResources pubblico (nome stringa). Chiedi al classloader una classe corrispondente a ciascun nome nel pacchetto che ti interessa. Ripetere l'operazione per tutti i programmi di caricamento classi pertinenti.

Sì, ma non è la cosa più semplice da fare. Ci sono molti problemi con questo. Non tutte le classi sono facili da trovare. Alcune classi potrebbero essere in un file: Jar, come file di classe, in rete ecc.

Dai un'occhiata in questa discussione.

Per essere sicuri che fossero del tipo ICommand, dovresti usare la riflessione per verificare la classe ereditaria.

Questo sarebbe uno strumento molto utile di cui abbiamo bisogno e JDK dovrebbe fornire supporto.

Ma probabilmente è meglio farlo durante la compilazione. Sai dove sono tutti i tuoi file di classe e puoi ispezionarli staticamente e creare un grafico. In fase di esecuzione è possibile eseguire una query su questo grafico per ottenere tutti i sottotipi. Ciò richiede più lavoro, ma credo che appartenga davvero al processo di creazione.

Utilizzando ClasspathSuite di Johannes Link , sono stato in grado di farlo in questo modo:

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 cerca file di classe e vasetti nel percorso di classe del sistema.

Nel tuo caso specifico, puoi modificare acceptClass in questo modo:

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

Una cosa da notare: fai attenzione a cosa restituisci in acceptClassName, come la prossima cosa che ClasspathClassesFinder fa è caricare la classe e chiamare acceptClass. Se acceptClassName restituisce sempre true, finirai per caricare ogni classe nel percorso di classe e ciò potrebbe causare un OutOfMemoryError.

Puoi utilizzare OpenPojo e fare questo:

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

Quindi puoi andare oltre l'elenco ed eseguire tutte le funzionalità che desideri.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top