Pergunta

Vamos dizer que eu tenho um commands pacote java que contém classes que todos herdam ICommand posso obter todas as classes de alguma forma? Estou bloqueio para algo entre as linhas de:

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

É algo como isso é possível?

Foi útil?

Solução

Aqui está um exemplo básico, assumindo que as aulas não são embalados-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$", "");
    Class<?> cls = Class.forName(packageName + "." + className);
    if (ICommand.class.isAssignableFrom(cls)) {
        commands.add((Class<ICommand>) cls);
    }
}

Outras dicas

A seguir, uma aplicação utilizando a API JSR-199 ou seja, aulas 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());
}

Aqui está um método de utilitário, usando Spring.

Detalhes sobre o padrão pode ser encontrada aqui

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

Comece com Classloader.getResources públicas (String name). Pergunte ao carregador de classe para uma classe correspondente a cada nome no pacote que você está interessado. Repita o procedimento para todos os carregadores de classe de relevância.

Sim, mas não é a coisa mais fácil de fazer. Há lotes de problemas com isso. Nem todas as classes são fáceis de encontrar. Algumas classes poderia estar em um:. Jar, como um arquivo de classe, através da rede etc

a esta discussão.

Para se certificar de que eles eram do tipo ICommand, em seguida, você teria que usar o reflexo para verificar se a classe herdando.

Esta seria uma ferramenta muito útil que precisamos, e JDK deve fornecer algum apoio.

Mas é provavelmente melhor feito durante a construção. Você sabe onde todos os seus arquivos de classe são e você pode inspecioná-los estaticamente e construir um gráfico. Em tempo de execução você pode consultar este gráfico para obter todos os subtipos. Isso requer mais trabalho, mas eu acredito que realmente pertence ao processo de construção.

Usando de Johannes Fazer a ligação ClasspathSuite , eu era capaz de fazê-lo como este:

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

Os olhares ClasspathClassesFinder para arquivos de classe e frascos no classpath do sistema.

No seu caso específico, você pode modificar acceptClass assim:

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

Uma coisa a nota: cuidado com o que você retornar em acceptClassName, como a próxima coisa ClasspathClassesFinder faz é carregar a classe e chamar acceptClass. Se acceptClassName sempre retornar true, você vai acabar de carregar todas as classes no classpath e que pode causar um OutOfMemoryError.

Você pode usar OpenPojo e fazer isso:

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

Então você pode ir sobre a lista e executar qualquer funcionalidade que você deseja.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top