Você pode encontrar todas as classes em um pacote usando reflexão?

528

É possível encontrar todas as classes ou interfaces em um determinado pacote? (Olhando rapidamente para Package, por exemplo , pareceria não.)

Jonik
fonte
2
Para sua informação, a solução Amit está vinculada a obras, embora haja um erro se o caminho da classe tiver um caractere de espaço (e provavelmente também para outros caracteres não alfanuméricos). se você o estiver usando em qualquer tipo de código de produção, consulte meu comentário na resposta dele para uma solução alternativa.
Kip
2
Observe também este post .
barfuin
1
Veja resposta relacionada: stackoverflow.com/a/30149061/4102160
Cfx
1
Observe também este post .
Sp00m
1
Veja minha resposta abaixo sobre o ClassGraph, atualmente é o método mais robusto para verificar o caminho de classe e o caminho do módulo.
Luke Hutchison

Respostas:

373

Devido à natureza dinâmica dos carregadores de classes, isso não é possível. Os carregadores de classes não precisam informar à VM quais classes ela pode oferecer; em vez disso, são apenas solicitações de classe recebidas e precisam retornar uma classe ou lançar uma exceção.

No entanto, se você escrever seus próprios carregadores de classe ou examinar os caminhos de classe e seus jars, é possível encontrar essas informações. Isso ocorrerá através de operações do sistema de arquivos e não de reflexão. Pode até haver bibliotecas que podem ajudá-lo a fazer isso.

Se houver classes geradas ou entregues remotamente, você não poderá descobrir essas classes.

Em vez disso, o método normal é registrar em algum lugar as classes às quais você precisa acessar em um arquivo ou referenciá-las em uma classe diferente. Ou apenas use a convenção quando se trata de nomear.

Adendo: A Biblioteca de Reflexões permitirá que você procure classes no caminho de classe atual. Pode ser usado para obter todas as classes em um pacote:

 Reflections reflections = new Reflections("my.project.prefix");

 Set<Class<? extends Object>> allClasses = 
     reflections.getSubTypesOf(Object.class);
Staale
fonte
12
A incapacidade de consultar nomes de classes me incomoda há muito tempo. Certamente, é difícil e o desempenho pode variar amplamente, e para certos carregadores de classe a lista é indefinida ou ilimitada, mas há maneiras de contornar isso.
Mr. Shiny and New #: 06/02/09
16
Observe que esta solução não funcionará, pois por padrão, getSubTypesOf não retorna subtipos de Object. Consulte a solução de Aleksander Blomskøld para saber como configurar o SubTypeScanner.
quer
17
Reflexões requer goiaba. Goiaba é grande. A versão 14.0.1 é 2.1MB.
mike jones
3
não funcionou para mim. Mac OSX - Dependência do Reflections versão 0.9.9-RC1 (maven) - JDK 1.7. Reconsidere a resposta aceita. @ AleksanderBlomskøld resposta é o único a ir. !!!!!
Konstantinos Margaritis
68
Se isso retornar uma lista vazia, inicialize o objeto Reflexos assim: Reflexões reflexões = novas reflexões ("your.package.here", new SubTypesScanner (false));
João Rocha da Silva
186

Você provavelmente deveria dar uma olhada na biblioteca de código-fonte do Reflections . Com ele, você pode facilmente alcançar o que deseja.

Primeiro, configure o índice de reflexões (é um pouco confuso, pois a pesquisa de todas as classes está desativada por padrão):

List<ClassLoader> classLoadersList = new LinkedList<ClassLoader>();
classLoadersList.add(ClasspathHelper.contextClassLoader());
classLoadersList.add(ClasspathHelper.staticClassLoader());

Reflections reflections = new Reflections(new ConfigurationBuilder()
    .setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
    .setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])))
    .filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("org.your.package"))));

Em seguida, você pode consultar todos os objetos em um determinado pacote:

Set<Class<?>> classes = reflections.getSubTypesOf(Object.class);
Aleksander Blomskøld
fonte
6
Ah, aqui vamos nós: code.google.com/p/reflections/issues/detail?id=122 . O objeto é excluído por padrão, mas você pode reativá-lo. Obrigado por me apontar para esta biblioteca, é ótimo!
Mtrc
1
Corri para problemas no meu Mac com esse código (relacionado a bibliotecas nativas), mas o uso ao .addUrls(ClasspathHelper.forJavaClassPath())invés do acima os resolveu para mim. Menos código também!
David Pärsson
3
se alguém quiser saber que a maneira mais simples de obter o pacote padrão é ter o prefixo como uma String vazia -> "".
JBA 17/10
2
A biblioteca "Reflections" possui uma licença complicada: github.com/ronmamo/reflections/blob/master/COPYING.txt . O truque é que a licença permite o uso gratuito apenas da própria licença. Portanto, para realmente usar a biblioteca (não a licença), todos devem entrar em contato com o autor e negociar os termos de uso.
Serge Rogatch 17/02
3
Serge, eu acho que você não entendeu WTFPL: wtfpl.net eu acho meio WTFPL que você é livre para fazer o que quiser, não apenas com a licença, mas com o código aswell
Richo
122

O Google Guava 14 inclui uma nova classe ClassPathcom três métodos para procurar classes de nível superior:

  • getTopLevelClasses()
  • getTopLevelClasses(String packageName)
  • getTopLevelClassesRecursive(String packageName)

Veja os ClassPathjavadocs para mais informações.

Christoph Leiter
fonte
Isso funcionou para mim onde a reflexão não podia fazer (sem ancestral direto comum, nenhuma anotação comum)
Riccardo Cossu
1
Como mencionei em um comentário abaixo , ClassPathé marcado com @Beta, por isso pode não ser uma boa idéia para alguns ...
Christian
1
Dizer que isso funciona onde a reflexão não funciona é um pouco estranho, sem dúvida a solução é implementada usando a funcionalidade de reflexão (e carregador de classes).
Maarten Bodewes
5
Acho que ele quis dizer a biblioteca de Reflexões mencionada na outra resposta.
Christoph Leiter
Funciona no Java 11, se estiver usando a versão 28.1-jre da goiaba.
gorjanz
112

Você pode usar este método 1 que usa o ClassLoader.

/**
 * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
 *
 * @param packageName The base package
 * @return The classes
 * @throws ClassNotFoundException
 * @throws IOException
 */
private static Class[] getClasses(String packageName)
        throws ClassNotFoundException, IOException {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    assert classLoader != null;
    String path = packageName.replace('.', '/');
    Enumeration<URL> resources = classLoader.getResources(path);
    List<File> dirs = new ArrayList<File>();
    while (resources.hasMoreElements()) {
        URL resource = resources.nextElement();
        dirs.add(new File(resource.getFile()));
    }
    ArrayList<Class> classes = new ArrayList<Class>();
    for (File directory : dirs) {
        classes.addAll(findClasses(directory, packageName));
    }
    return classes.toArray(new Class[classes.size()]);
}

/**
 * Recursive method used to find all classes in a given directory and subdirs.
 *
 * @param directory   The base directory
 * @param packageName The package name for classes found inside the base directory
 * @return The classes
 * @throws ClassNotFoundException
 */
private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
    List<Class> classes = new ArrayList<Class>();
    if (!directory.exists()) {
        return classes;
    }
    File[] files = directory.listFiles();
    for (File file : files) {
        if (file.isDirectory()) {
            assert !file.getName().contains(".");
            classes.addAll(findClasses(file, packageName + "." + file.getName()));
        } else if (file.getName().endsWith(".class")) {
            classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
        }
    }
    return classes;
}

__________
1 Esse método foi obtido originalmente em http://snippets.dzone.com/posts/show/4831 , que foi arquivado pelo Internet Archive, como vinculado agora. O snippet também está disponível em https://dzone.com/articles/get-all-classes-within-package .

Ahmed Ashour
fonte
6
Eu tive um problema com isso se meu caminho incluísse espaços. A classe de URL estava escapando de espaços para %20, mas o new File()construtor tratou isso como um sinal de porcentagem literal dois zero. Corrigi-o alterando a dirs.add(...)linha para o seguinte: dirs.add(new File(resource.toURI())); Isso também significava que eu tinha que adicionar URISyntaxExceptionà cláusula throws degetClasses
Kip 16/02/10
19
Você acabou de copiar de dzone.com/articles/get-all-classes-within-package ! consulte a fonte na próxima vez
RS
19
+1 porque esta solução NÃO requer bibliotecas externas ... NUNCA, NUNCA realmente junte seu código aleatoriamente com bibliotecas apenas para conseguir uma coisa pequena como essa. você sabe que está adicionando uma superfície de ataque em potencial para atacantes? Em novembro de 2015, foi descoberto um problema no Apache Commons que leva à Execução Remota de Comando, apenas com o Apache Commons no caminho de classe de um aplicativo implantado no Jboss / Weblogic [ foxglovesecurity.com/2015/11/06/…
sc0p 6/15/15
O @Qix observou corretamente que esse código não suporta jar. Para suportar frascos e diretórios . O código foi alterado conforme observado abaixo:
user1523177 8/16
1
Boa solução, mas parece ser melhor se 'Class.forName (String className)' for substituído por 'Class.forName (String className, inicialização booleana, carregador de ClassLoader)' onde 'initialize = false;' para não criar instâncias de classe.
Andrei_N 20/05
99

Primavera

Este exemplo é para o Spring 4, mas você pode encontrar o scanner de caminho de classe em versões anteriores.

// create scanner and disable default filters (that is the 'false' argument)
final ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
// add include filters which matches all the classes (or use your own)
provider.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*")));

// get matching classes defined in the package
final Set<BeanDefinition> classes = provider.findCandidateComponents("my.package.name");

// this is how you can load the class type from BeanDefinition instance
for (BeanDefinition bean: classes) {
    Class<?> clazz = Class.forName(bean.getBeanClassName());
    // ... do your magic with the class ...
}

Google Guava

Nota: Na versão 14, a API ainda está marcada como @Beta , portanto, tenha cuidado no código de produção.

final ClassLoader loader = Thread.currentThread().getContextClassLoader();

for (final ClassPath.ClassInfo info : ClassPath.from(loader).getTopLevelClasses()) {
  if (info.getName().startsWith("my.package.")) {
    final Class<?> clazz = info.load();
    // do something with your clazz
  }
}
voho
fonte
5
Excelente resposta. Existem muitas soluções aqui que são detalhadas, não testadas, não funcionam! Este é fantástico: é conciso e testado (é da Goiaba). Muito bom! É útil, merece mais votos.
precisa saber é o seguinte
Infelizmente, a ClassPathclasse no Guava também está marcada com @Beta: "As APIs marcadas com a anotação @Beta no nível da classe ou do método estão sujeitas a alterações. Elas podem ser modificadas de qualquer forma ou mesmo removidas em qualquer release principal. Se o seu código é uma biblioteca em si (ou seja, é usada no CLASSPATH de usuários fora de seu próprio controle), você não deve usar APIs beta, a menos que as reembale
Christian
@ Christian Bom ponto, eu não notei! Obrigado. Adicionarei outra resposta usando o scanner de caminho de classe do Spring, que não é beta com certeza.
voho 9/01/15
Para encontrar classes estáticas aninhadas usando a solução goiaba, o getAllClasses()método pode ser usado.
v.ladynev
1
A solução Spring é a única que funciona se for executada em um jar executável.
Lucas
38

Olá. Eu sempre tive alguns problemas com as soluções acima (e em outros sites).
Eu, como desenvolvedor, estou programando um complemento para uma API. A API impede o uso de bibliotecas externas ou ferramentas de terceiros. A instalação também consiste em uma mistura de código em arquivos jar ou zip e arquivos de classe localizados diretamente em alguns diretórios. Portanto, meu código precisava funcionar em todas as configurações. Depois de muita pesquisa, criei um método que funcionará em pelo menos 95% de todas as configurações possíveis.

O código a seguir é basicamente o método de exagero que sempre funcionará.

O código:

Esse código verifica um determinado pacote para todas as classes incluídas nele. Ele funcionará apenas para todas as classes na atual ClassLoader.

/**
 * Private helper method
 * 
 * @param directory
 *            The directory to start with
 * @param pckgname
 *            The package name to search for. Will be needed for getting the
 *            Class object.
 * @param classes
 *            if a file isn't loaded but still is in the directory
 * @throws ClassNotFoundException
 */
private static void checkDirectory(File directory, String pckgname,
        ArrayList<Class<?>> classes) throws ClassNotFoundException {
    File tmpDirectory;

    if (directory.exists() && directory.isDirectory()) {
        final String[] files = directory.list();

        for (final String file : files) {
            if (file.endsWith(".class")) {
                try {
                    classes.add(Class.forName(pckgname + '.'
                            + file.substring(0, file.length() - 6)));
                } catch (final NoClassDefFoundError e) {
                    // do nothing. this class hasn't been found by the
                    // loader, and we don't care.
                }
            } else if ((tmpDirectory = new File(directory, file))
                    .isDirectory()) {
                checkDirectory(tmpDirectory, pckgname + "." + file, classes);
            }
        }
    }
}

/**
 * Private helper method.
 * 
 * @param connection
 *            the connection to the jar
 * @param pckgname
 *            the package name to search for
 * @param classes
 *            the current ArrayList of all classes. This method will simply
 *            add new classes.
 * @throws ClassNotFoundException
 *             if a file isn't loaded but still is in the jar file
 * @throws IOException
 *             if it can't correctly read from the jar file.
 */
private static void checkJarFile(JarURLConnection connection,
        String pckgname, ArrayList<Class<?>> classes)
        throws ClassNotFoundException, IOException {
    final JarFile jarFile = connection.getJarFile();
    final Enumeration<JarEntry> entries = jarFile.entries();
    String name;

    for (JarEntry jarEntry = null; entries.hasMoreElements()
            && ((jarEntry = entries.nextElement()) != null);) {
        name = jarEntry.getName();

        if (name.contains(".class")) {
            name = name.substring(0, name.length() - 6).replace('/', '.');

            if (name.contains(pckgname)) {
                classes.add(Class.forName(name));
            }
        }
    }
}

/**
 * Attempts to list all the classes in the specified package as determined
 * by the context class loader
 * 
 * @param pckgname
 *            the package name to search
 * @return a list of classes that exist within that package
 * @throws ClassNotFoundException
 *             if something went wrong
 */
public static ArrayList<Class<?>> getClassesForPackage(String pckgname)
        throws ClassNotFoundException {
    final ArrayList<Class<?>> classes = new ArrayList<Class<?>>();

    try {
        final ClassLoader cld = Thread.currentThread()
                .getContextClassLoader();

        if (cld == null)
            throw new ClassNotFoundException("Can't get class loader.");

        final Enumeration<URL> resources = cld.getResources(pckgname
                .replace('.', '/'));
        URLConnection connection;

        for (URL url = null; resources.hasMoreElements()
                && ((url = resources.nextElement()) != null);) {
            try {
                connection = url.openConnection();

                if (connection instanceof JarURLConnection) {
                    checkJarFile((JarURLConnection) connection, pckgname,
                            classes);
                } else if (connection instanceof FileURLConnection) {
                    try {
                        checkDirectory(
                                new File(URLDecoder.decode(url.getPath(),
                                        "UTF-8")), pckgname, classes);
                    } catch (final UnsupportedEncodingException ex) {
                        throw new ClassNotFoundException(
                                pckgname
                                        + " does not appear to be a valid package (Unsupported encoding)",
                                ex);
                    }
                } else
                    throw new ClassNotFoundException(pckgname + " ("
                            + url.getPath()
                            + ") does not appear to be a valid package");
            } catch (final IOException ioex) {
                throw new ClassNotFoundException(
                        "IOException was thrown when trying to get all resources for "
                                + pckgname, ioex);
            }
        }
    } catch (final NullPointerException ex) {
        throw new ClassNotFoundException(
                pckgname
                        + " does not appear to be a valid package (Null pointer exception)",
                ex);
    } catch (final IOException ioex) {
        throw new ClassNotFoundException(
                "IOException was thrown when trying to get all resources for "
                        + pckgname, ioex);
    }

    return classes;
}

Esses três métodos fornecem a capacidade de encontrar todas as classes em um determinado pacote.
Você o usa assim:

getClassesForPackage("package.your.classes.are.in");

A explicação:

O método primeiro obtém o atual ClassLoader. Em seguida, ele busca todos os recursos que contêm o referido pacote e itera esses URLs. Em seguida, cria um URLConnectione determina que tipo de URl temos. Pode ser um diretório ( FileURLConnection) ou um diretório dentro de um arquivo jar ou zip ( JarURLConnection). Dependendo do tipo de conexão, temos dois métodos diferentes que serão chamados.

Primeiro vamos ver o que acontece se for um FileURLConnection.
Ele primeiro verifica se o arquivo passado existe e é um diretório. Se for esse o caso, verifica se é um arquivo de classe. Nesse caso, um Classobjeto será criado e colocado no ArrayList. Se não é um arquivo de classe, mas é um diretório, simplesmente iteramos nele e fazemos a mesma coisa. Todos os outros casos / arquivos serão ignorados.

Se o URLConnectioné, JarURLConnectiono outro método auxiliar privado será chamado. Esse método itera sobre todas as entradas no arquivo zip / jar. Se uma entrada for um arquivo de classe e estiver dentro do pacote, um Classobjeto será criado e armazenado no ArrayList.

Depois que todos os recursos foram analisados, o método principal retorna o ArrayListconteúdo de todas as classes no pacote fornecido, que os atuais ClassLoaderconhecem.

Se o processo falhar a qualquer momento ClassNotFoundException, será lançado um contendo informações detalhadas sobre a causa exata.

BrainStone
fonte
4
Este exemplo parece exigir importação sun.net.www.protocol.file.FileURLConnection, o que gera um aviso em tempo de compilação ("warning: sun.net.www.protocol.file.FileURLConnection é API proprietária da Sun e pode ser removido em uma versão futura"). Existe uma alternativa ao uso dessa classe ou o aviso pode ser suprimido usando anotações?
Christian
Esse método não funciona para classes de autoinicialização, como as de java.lang, java.util, ... Essas podem ser encontradas ao obter System.getProperty ("sun.boot.class.path"), dividindo com: ou; (dependendo do sistema operacional) e, em seguida, executando versões ligeiramente modificadas do checkDirectory e checkJarFile acima.
Coderforlife
1
Você pode contornar o aviso / erro usando connection.getClass (). GetCanonicalName (). Equals ("sun.net.www.protocol.file.FileURLConnection"). Se você realmente deseja, pode criar uma URLConnection que você acha que DEVE usar sun.net.www.protocol.file.FileURLConnection e também comparar o nome da classe de conexão com o nome da classe que você criou. Se os dois são iguais, você pode tratá-lo como uma instância do sun.net.www.protocol.file.FileURLConnection, em vez de falhar, caso o nome da classe seja alterado.
William Deans
1
@Christian Você pode evitar o FileURLConnection fazendo algo parecido com isto: if ( ... instanceof JarURLConnecton) { ... } else { // Asume that the Connection is valid and points to a File }É o que eu fiz no meu código para procurar JPA anotada aulas
Zardoz89
15

Sem usar nenhuma biblioteca extra:

package test;

import java.io.DataInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) throws Exception{
        List<Class> classes = getClasses(Test.class.getClassLoader(),"test");
        for(Class c:classes){
            System.out.println("Class: "+c);
        }
    }

    public static List<Class> getClasses(ClassLoader cl,String pack) throws Exception{

        String dottedPackage = pack.replaceAll("[/]", ".");
        List<Class> classes = new ArrayList<Class>();
        URL upackage = cl.getResource(pack);

        DataInputStream dis = new DataInputStream((InputStream) upackage.getContent());
        String line = null;
        while ((line = dis.readLine()) != null) {
            if(line.endsWith(".class")) {
               classes.add(Class.forName(dottedPackage+"."+line.substring(0,line.lastIndexOf('.'))));
            }
        }
        return classes;
    }
}
Williams López
fonte
Quando eu executo isso em um JAR, upackageé null... :(
Christian
Para um pacote "com.mycompany.beans", substitua "test" por "com / mycompany / beans"
James Jithin 17/03/16
4
Eu recebo um nulo ao usar esse código. Parece só funcionam se o seu jar é um executável
Alao
se você obteve o nome do pacote String pack = getPackage().getName();, precisará adicionarpack = pack.replaceAll("[.]", "/");
user2682877
13

Em geral, os carregadores de classes não permitem a varredura em todas as classes no caminho de classe. Mas geralmente o único carregador de classes usado é o UrlClassLoader, do qual podemos recuperar a lista de diretórios e arquivos jar (consulte getURLs ) e abri-los um por um para listar as classes disponíveis. Essa abordagem, chamada de rastreamento de caminho de classe, é implementada no Scannotation and Reflections .

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

Outra abordagem é usar a API do Java Pluggable Annotation Processing para gravar o processador de anotações que coletará todas as classes anotadas em tempo de compilação e criará o arquivo de índice para uso em tempo de execução. Este mecanismo é implementado na biblioteca ClassIndex :

// package-info.java
@IndexSubclasses
package my.package;

// your code
Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");

Observe que nenhuma configuração adicional é necessária, pois a verificação é totalmente automatizada, graças ao compilador Java que descobre automaticamente todos os processadores encontrados no caminho de classe.

Sławek
fonte
isso descobre classes empacotadas em um jar? Não parece funcionar para mim.
ASGs
qual ferramenta você está tentando usar?
Sławek
Estou usando a biblioteca do Reflections. Mas consegui funcionar depois de seguir a solução mencionada por @Aleksander Blomskøld para versões recentes desta biblioteca.
ASGs
Oi, eu estou usando o eclipse e não consigo fazê-lo funcionar, ClassIndex.getPackageClasses ("my.package") retorna um mapa vazio
Juan
11

O mecanismo mais robusto para listar todas as classes em um determinado pacote atualmente é o ClassGraph , porque ele lida com a maior variedade possível de mecanismos de especificação de caminho de classe , incluindo o novo sistema de módulo JPMS. (Eu sou o autor.)

List<String> classNames = new ArrayList<>();
try (ScanResult scanResult = new ClassGraph().whitelistPackages("my.package")
        .enableClassInfo().scan()) {
    classNames.addAll(scanResult.getAllClasses().getNames());
}
Luke Hutchison
fonte
5

Aqui está como eu faço isso. Examino todas as subpastas (subpacotes) e não tento carregar classes anônimas:

   /**
   * Attempts to list all the classes in the specified package as determined
   * by the context class loader, recursively, avoiding anonymous classes
   * 
   * @param pckgname
   *            the package name to search
   * @return a list of classes that exist within that package
   * @throws ClassNotFoundException
   *             if something went wrong
   */
  private static List<Class> getClassesForPackage(String pckgname) throws ClassNotFoundException {
      // This will hold a list of directories matching the pckgname. There may be more than one if a package is split over multiple jars/paths
      ArrayList<File> directories = new ArrayList<File>();
      String packageToPath = pckgname.replace('.', '/');
      try {
          ClassLoader cld = Thread.currentThread().getContextClassLoader();
          if (cld == null) {
              throw new ClassNotFoundException("Can't get class loader.");
          }

          // Ask for all resources for the packageToPath
          Enumeration<URL> resources = cld.getResources(packageToPath);
          while (resources.hasMoreElements()) {
              directories.add(new File(URLDecoder.decode(resources.nextElement().getPath(), "UTF-8")));
          }
      } catch (NullPointerException x) {
          throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Null pointer exception)");
      } catch (UnsupportedEncodingException encex) {
          throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Unsupported encoding)");
      } catch (IOException ioex) {
          throw new ClassNotFoundException("IOException was thrown when trying to get all resources for " + pckgname);
      }

      ArrayList<Class> classes = new ArrayList<Class>();
      // For every directoryFile identified capture all the .class files
      while (!directories.isEmpty()){
          File directoryFile  = directories.remove(0);             
          if (directoryFile.exists()) {
              // Get the list of the files contained in the package
              File[] files = directoryFile.listFiles();

              for (File file : files) {
                  // we are only interested in .class files
                  if ((file.getName().endsWith(".class")) && (!file.getName().contains("$"))) {
                      // removes the .class extension
                      int index = directoryFile.getPath().indexOf(packageToPath);
                      String packagePrefix = directoryFile.getPath().substring(index).replace('/', '.');;                          
                    try {                  
                      String className = packagePrefix + '.' + file.getName().substring(0, file.getName().length() - 6);                            
                      classes.add(Class.forName(className));                                
                    } catch (NoClassDefFoundError e)
                    {
                      // do nothing. this class hasn't been found by the loader, and we don't care.
                    }
                  } else if (file.isDirectory()){ // If we got to a subdirectory
                      directories.add(new File(file.getPath()));                          
                  }
              }
          } else {
              throw new ClassNotFoundException(pckgname + " (" + directoryFile.getPath() + ") does not appear to be a valid package");
          }
      }
      return classes;
  }  
Nadav B
fonte
4

Eu montei um projeto simples do github que resolve esse problema:

https://github.com/ddopson/java-class-enumerator

Ele deve funcionar para AMBOS os caminhos de classe baseados em arquivo E para arquivos jar.

Se você executar 'make' depois de verificar o projeto, ele será impresso:

 Cleaning...
rm -rf build/
 Building...
javac -d build/classes src/pro/ddopson/ClassEnumerator.java src/test/ClassIShouldFindOne.java src/test/ClassIShouldFindTwo.java src/test/subpkg/ClassIShouldFindThree.java src/test/TestClassEnumeration.java
 Making JAR Files...
jar cf build/ClassEnumerator_test.jar -C build/classes/ . 
jar cf build/ClassEnumerator.jar -C build/classes/ pro
 Running Filesystem Classpath Test...
java -classpath build/classes test.TestClassEnumeration
ClassDiscovery: Package: 'test' becomes Resource: 'file:/Users/Dopson/work/other/java-class-enumeration/build/classes/test'
ClassDiscovery: Reading Directory '/Users/Dopson/work/other/java-class-enumeration/build/classes/test'
ClassDiscovery: FileName 'ClassIShouldFindOne.class'  =>  class 'test.ClassIShouldFindOne'
ClassDiscovery: FileName 'ClassIShouldFindTwo.class'  =>  class 'test.ClassIShouldFindTwo'
ClassDiscovery: FileName 'subpkg'  =>  class 'null'
ClassDiscovery: Reading Directory '/Users/Dopson/work/other/java-class-enumeration/build/classes/test/subpkg'
ClassDiscovery: FileName 'ClassIShouldFindThree.class'  =>  class 'test.subpkg.ClassIShouldFindThree'
ClassDiscovery: FileName 'TestClassEnumeration.class'  =>  class 'test.TestClassEnumeration'
 Running JAR Classpath Test...
java -classpath build/ClassEnumerator_test.jar  test.TestClassEnumeration
ClassDiscovery: Package: 'test' becomes Resource: 'jar:file:/Users/Dopson/work/other/java-class-enumeration/build/ClassEnumerator_test.jar!/test'
ClassDiscovery: Reading JAR file: '/Users/Dopson/work/other/java-class-enumeration/build/ClassEnumerator_test.jar'
ClassDiscovery: JarEntry 'META-INF/'  =>  class 'null'
ClassDiscovery: JarEntry 'META-INF/MANIFEST.MF'  =>  class 'null'
ClassDiscovery: JarEntry 'pro/'  =>  class 'null'
ClassDiscovery: JarEntry 'pro/ddopson/'  =>  class 'null'
ClassDiscovery: JarEntry 'pro/ddopson/ClassEnumerator.class'  =>  class 'null'
ClassDiscovery: JarEntry 'test/'  =>  class 'null'
ClassDiscovery: JarEntry 'test/ClassIShouldFindOne.class'  =>  class 'test.ClassIShouldFindOne'
ClassDiscovery: JarEntry 'test/ClassIShouldFindTwo.class'  =>  class 'test.ClassIShouldFindTwo'
ClassDiscovery: JarEntry 'test/subpkg/'  =>  class 'null'
ClassDiscovery: JarEntry 'test/subpkg/ClassIShouldFindThree.class'  =>  class 'test.subpkg.ClassIShouldFindThree'
ClassDiscovery: JarEntry 'test/TestClassEnumeration.class'  =>  class 'test.TestClassEnumeration'
 Tests Passed. 

Veja também minha outra resposta

Dave Dopson
fonte
4

Sim, usando poucas APIs que você pode, aqui está como eu gosto de fazê-lo, enfrentou esse problema que eu estava usando o hibernate core e tive que encontrar classes que foram anotadas com uma certa anotação.

Crie uma anotação personalizada usando a qual você marcará quais classes deseja selecionar.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface EntityToBeScanned {

}

Em seguida, marque sua classe com ela como

@EntityToBeScanned 
public MyClass{

}

Crie esta classe de utilitário que possui o seguinte método

public class ClassScanner {

    public static Set<Class<?>> allFoundClassesAnnotatedWithEntityToBeScanned(){
        Reflections reflections = new Reflections(".*");
        Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(EntityToBeScanned.class);
        return annotated;
    }

}

Chame o método allFoundClassesAnnotatedWithEntityToBeScanned () para obter um conjunto de classes encontrado.

Você precisará das bibliotecas fornecidas abaixo

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>21.0</version>
    </dependency>
<!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.22.0-CR1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.9.10</version>
</dependency>
Sujal Mandal
fonte
3

Você precisa procurar todas as entradas do carregador de classes no caminho da classe:

    String pkg = "org/apache/commons/lang";
    ClassLoader cl = ClassLoader.getSystemClassLoader();
    URL[] urls = ((URLClassLoader) cl).getURLs();
    for (URL url : urls) {
        System.out.println(url.getFile());
        File jar = new File(url.getFile());
        // ....
    }   

Se a entrada for diretório, basta procurar no subdiretório direito:

if (jar.isDirectory()) {
    File subdir = new File(jar, pkg);
    if (!subdir.exists())
        continue;
    File[] files = subdir.listFiles();
    for (File file : files) {
        if (!file.isFile())
            continue;
        if (file.getName().endsWith(".class"))
            System.out.println("Found class: "
                    + file.getName().substring(0,
                            file.getName().length() - 6));
    }
}   

Se a entrada é o arquivo e é jar, inspecione as entradas ZIP dele:

else {
    // try to open as ZIP
    try {
        ZipFile zip = new ZipFile(jar);
        for (Enumeration<? extends ZipEntry> entries = zip
                .entries(); entries.hasMoreElements();) {
            ZipEntry entry = entries.nextElement();
            String name = entry.getName();
            if (!name.startsWith(pkg))
                continue;
            name = name.substring(pkg.length() + 1);
            if (name.indexOf('/') < 0 && name.endsWith(".class"))
                System.out.println("Found class: "
                        + name.substring(0, name.length() - 6));
        }
    } catch (ZipException e) {
        System.out.println("Not a ZIP: " + e.getMessage());
    } catch (IOException e) {
        System.err.println(e.getMessage());
    }
}

Agora, depois de ter todos os nomes de classe dentro do pacote, você pode tentar carregá-los com reflexão e analisar se eles são classes ou interfaces, etc.

Marinheiro Danubiano
fonte
O que você digitaria para um pacote em um arquivo Jar?
Kyle Bridenstine
Este exemplo não passará por subpacotes. Talvez isso seja do interesse de alguns ... @ mr-tea Apenas especifique o pacote que você está procurando. Coloquei isso em um projeto, especifiquei um pacote de teste nesse projeto, o compilei e empacotei e chamei o exemplo de método principal do JAR. Funcionou como um encanto. :)
Christian
3

Eu tenho tentado usar a biblioteca Reflections, mas tive alguns problemas usando-a, e havia muitos jarros que eu deveria incluir apenas para simplesmente obter as classes em um pacote.

Vou postar uma solução que encontrei nesta pergunta duplicada: Como obter todos os nomes de classes em um pacote?

A resposta foi escrita por sp00m ; Eu adicionei algumas correções para fazê-lo funcionar:

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;

public final class ClassFinder {

    private final static char DOT = '.';
    private final static char SLASH = '/';
    private final static String CLASS_SUFFIX = ".class";
    private final static String BAD_PACKAGE_ERROR = "Unable to get resources from path '%s'. Are you sure the given '%s' package exists?";

    public final static List<Class<?>> find(final String scannedPackage) {
        final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        final String scannedPath = scannedPackage.replace(DOT, SLASH);
        final Enumeration<URL> resources;
        try {
            resources = classLoader.getResources(scannedPath);
        } catch (IOException e) {
            throw new IllegalArgumentException(String.format(BAD_PACKAGE_ERROR, scannedPath, scannedPackage), e);
        }
        final List<Class<?>> classes = new LinkedList<Class<?>>();
        while (resources.hasMoreElements()) {
            final File file = new File(resources.nextElement().getFile());
            classes.addAll(find(file, scannedPackage));
        }
        return classes;
    }

    private final static List<Class<?>> find(final File file, final String scannedPackage) {
        final List<Class<?>> classes = new LinkedList<Class<?>>();
        if (file.isDirectory()) {
            for (File nestedFile : file.listFiles()) {
                classes.addAll(find(nestedFile, scannedPackage));
            }
        //File names with the $1, $2 holds the anonymous inner classes, we are not interested on them. 
        } else if (file.getName().endsWith(CLASS_SUFFIX) && !file.getName().contains("$")) {

            final int beginIndex = 0;
            final int endIndex = file.getName().length() - CLASS_SUFFIX.length();
            final String className = file.getName().substring(beginIndex, endIndex);
            try {
                final String resource = scannedPackage + DOT + className;
                classes.add(Class.forName(resource));
            } catch (ClassNotFoundException ignore) {
            }
        }
        return classes;
    }

}

Para usá-lo, basta chamar o método find conforme sp00n mencionado neste exemplo: Adicionei a criação de instâncias das classes, se necessário.

List<Class<?>> classes = ClassFinder.find("com.package");

ExcelReporting excelReporting;
for (Class<?> aClass : classes) {
    Constructor constructor = aClass.getConstructor();
    //Create an object of the class type
    constructor.newInstance();
    //...
}
Martín C
fonte
3

Acabei de escrever uma classe util, que inclui métodos de teste, você pode verificar ~

IteratePackageUtil.java:

package eric.j2se.reflect;

import java.util.Set;

import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;

/**
 * an util to iterate class in a package,
 * 
 * @author eric
 * @date Dec 10, 2013 12:36:46 AM
 */
public class IteratePackageUtil {
    /**
     * <p>
     * Get set of all class in a specified package recursively. this only support lib
     * </p>
     * <p>
     * class of sub package will be included, inner class will be included,
     * </p>
     * <p>
     * could load class that use the same classloader of current class, can't load system packages,
     * </p>
     * 
     * @param pkg
     *            path of a package
     * @return
     */
    public static Set<Class<? extends Object>> getClazzSet(String pkg) {
        // prepare reflection, include direct subclass of Object.class
        Reflections reflections = new Reflections(new ConfigurationBuilder().setScanners(new SubTypesScanner(false), new ResourcesScanner())
                .setUrls(ClasspathHelper.forClassLoader(ClasspathHelper.classLoaders(new ClassLoader[0])))
                .filterInputsBy(new FilterBuilder().includePackage(pkg)));

        return reflections.getSubTypesOf(Object.class);
    }

    public static void test() {
        String pkg = "org.apache.tomcat.util";

        Set<Class<? extends Object>> clazzSet = getClazzSet(pkg);
        for (Class<? extends Object> clazz : clazzSet) {
            System.out.println(clazz.getName());
        }
    }

    public static void main(String[] args) {
        test();
    }
}
Eric Wang
fonte
3

A solução de Aleksander Blomskøld não funcionou para mim em testes parametrizados @RunWith(Parameterized.class)ao usar o Maven. Os testes foram nomeados corretamente e também onde foram encontrados, mas não executados:

-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running some.properly.named.test.run.with.maven.SomeTest
Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.123 sec

Um problema semelhante foi relatado aqui .

No meu caso, @Parametersestá criando instâncias de cada classe em um pacote. Os testes funcionaram bem quando executados localmente no IDE. No entanto, ao executar o Maven, nenhuma classe foi encontrada com a solução de Aleksander Blomskøld.

Eu fiz funcionar com o seguinte trecho, inspirado no comentário de David Pärsson sobre a resposta de Aleksander Blomskøld:

Reflections reflections = new Reflections(new ConfigurationBuilder()
            .setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
            .addUrls(ClasspathHelper.forJavaClassPath()) 
            .filterInputsBy(new FilterBuilder()
            .include(FilterBuilder.prefix(basePackage))));

Set<Class<?>> subTypesOf = reflections.getSubTypesOf(Object.class);
Thorsten
fonte
3

Que tal isso:

public static List<Class<?>> getClassesForPackage(final String pkgName) throws IOException, URISyntaxException {
    final String pkgPath = pkgName.replace('.', '/');
    final URI pkg = Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResource(pkgPath)).toURI();
    final ArrayList<Class<?>> allClasses = new ArrayList<Class<?>>();

    Path root;
    if (pkg.toString().startsWith("jar:")) {
        try {
            root = FileSystems.getFileSystem(pkg).getPath(pkgPath);
        } catch (final FileSystemNotFoundException e) {
            root = FileSystems.newFileSystem(pkg, Collections.emptyMap()).getPath(pkgPath);
        }
    } else {
        root = Paths.get(pkg);
    }

    final String extension = ".class";
    try (final Stream<Path> allPaths = Files.walk(root)) {
        allPaths.filter(Files::isRegularFile).forEach(file -> {
            try {
                final String path = file.toString().replace('/', '.');
                final String name = path.substring(path.indexOf(pkgName), path.length() - extension.length());
                allClasses.add(Class.forName(name));
            } catch (final ClassNotFoundException | StringIndexOutOfBoundsException ignored) {
            }
        });
    }
    return allClasses;
}

Você pode sobrecarregar a função:

public static List<Class<?>> getClassesForPackage(final Package pkg) throws IOException, URISyntaxException {
    return getClassesForPackage(pkg.getName());
}

Se você precisar testá-lo:

public static void main(final String[] argv) throws IOException, URISyntaxException {
    for (final Class<?> cls : getClassesForPackage("my.package")) {
        System.out.println(cls);
    }
    for (final Class<?> cls : getClassesForPackage(MyClass.class.getPackage())) {
        System.out.println(cls);
    }
}

Se o seu IDE não tiver um auxiliar de importação:

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;

Funciona:

  • do seu IDE

  • para um arquivo JAR

  • sem dependências externas

tirz
fonte
2

Quase todas as respostas que usa Reflections ou lê arquivos de classe do sistema de arquivos. Se você tentar ler classes do sistema de arquivos, poderá ocorrer erros ao compactar seu aplicativo como JAR ou outro. Além disso, você pode não querer usar uma biblioteca separada para esse fim.

Aqui está outra abordagem que é java puro e não depende do sistema de arquivos.

import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

public class PackageUtil {

    public static Collection<Class> getClasses(final String pack) throws Exception {
        final StandardJavaFileManager fileManager = ToolProvider.getSystemJavaCompiler().getStandardFileManager(null, null, null);
        return StreamSupport.stream(fileManager.list(StandardLocation.CLASS_PATH, pack, Collections.singleton(JavaFileObject.Kind.CLASS), false).spliterator(), false)
                .map(javaFileObject -> {
                    try {
                        final String[] split = javaFileObject.getName()
                                .replace(".class", "")
                                .replace(")", "")
                                .split(Pattern.quote(File.separator));

                        final String fullClassName = pack + "." + split[split.length - 1];
                        return Class.forName(fullClassName);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }

                })
                .collect(Collectors.toCollection(ArrayList::new));
    }
}

O Java 8 não é obrigatório . Você pode usar loops em vez de fluxos. E você pode testá-lo assim

public static void main(String[] args) throws Exception {
    final String pack = "java.nio.file"; // Or any other package
    PackageUtil.getClasses(pack).stream().forEach(System.out::println);
}
bhdrkn
fonte
1
Não é muito útil por causa de: é necessário que o JDK use ToolProvider.getSystemJavaCompiler(), esse código não verifica pacotes aninhados.
v.ladynev
Eu não posso fazê-lo trabalhar com um pacote de um frasco externo
Enrico Giurin
1

Desde que você não esteja usando nenhum carregador de classe dinâmico, é possível pesquisar o caminho de classe e, para cada entrada, pesquisar o diretório ou arquivo JAR.

Lawrence Dol
fonte
1

Vale a pena mencionar

Se você deseja ter uma lista de todas as classes em algum pacote, pode usar Reflectionda seguinte maneira:

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

Reflections reflections = new Reflections("com.package");
for (String s : reflections.getStore().get(SubTypesScanner.class).values()) {
    myTypes.add(Class.forName(s));
}

Isso criará uma lista de classes que mais tarde você poderá usá-las como desejar.

Maroun
fonte
1

É muito possível, mas sem bibliotecas adicionais, como Reflectionsé difícil ...
É difícil porque você não possui um instrumento completo para obter o nome da classe.
E eu pego o código da minha ClassFinderclasse:

package play.util;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Created by LINKOR on 26.05.2017 in 15:12.
 * Date: 2017.05.26
 */
public class FileClassFinder {
private JarFile file;
private boolean trouble;
public FileClassFinder(String filePath) {
    try {
        file = new JarFile(filePath);
    } catch (IOException e) {
        trouble = true;
    }
}

public List<String> findClasses(String pkg) {
    ArrayList<String> classes = new ArrayList<>();
    Enumeration<JarEntry> entries = file.entries();
    while (entries.hasMoreElements()) {
        JarEntry cls = entries.nextElement();
        if (!cls.isDirectory()) {
            String fileName = cls.getName();
            String className = fileName.replaceAll("/",         ".").replaceAll(File.pathSeparator, ".").substring(0, fileName.lastIndexOf('.'));
            if (className.startsWith(pkg)) classes.add(className.substring(pkg.length() + 1));
        }
    }
    return classes;
}
}
Muskovets
fonte
0

Com base na resposta de @ Staale e em uma tentativa de não confiar em bibliotecas de terceiros, eu implementaria a abordagem do Sistema de Arquivos inspecionando a localização física do primeiro pacote com:

import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
...
Class<?>[] foundClasses = new Class<?>[0];
final ArrayList<Class<?>> foundClassesDyn = new ArrayList<Class<?>>();

new java.io.File(
    klass.getResource(
        "/" + curPackage.replace( "." , "/")
    ).getFile()
).listFiles(
    new java.io.FileFilter() {
        public boolean accept(java.io.File file) {
            final String classExtension = ".class";

            if ( file.isFile()
                && file.getName().endsWith(classExtension)
                // avoid inner classes
                && ! file.getName().contains("$") )
            {
                try {
                    String className = file.getName();
                    className = className.substring(0, className.length() - classExtension.length());
                    foundClassesDyn.add( Class.forName( curPackage + "." + className ) );
                } catch (ClassNotFoundException e) {
                    e.printStackTrace(System.out);
                }
            }

            return false;
        }
    }
);

foundClasses = foundClassesDyn.toArray(foundClasses);
Comunidade
fonte
0

Se você deseja apenas carregar um grupo de classes relacionadas, o Spring pode ajudá-lo.

O Spring pode instanciar uma lista ou mapa de todas as classes que implementam uma determinada interface em uma linha de código. A lista ou mapa conterá instâncias de todas as classes que implementam essa interface.

Dito isso, como alternativa ao carregamento da lista de classes do sistema de arquivos, basta implementar a mesma interface em todas as classes que você deseja carregar, independentemente do pacote e usar o Spring para fornecer instâncias de todas elas. Dessa forma, você pode carregar (e instanciar) todas as classes que desejar, independentemente do pacote em que elas estejam.

Por outro lado, se você deseja tê-los todos em um pacote, basta fazer com que todas as classes nesse pacote implementem uma determinada interface.

Rodney P. Barbati
fonte
0

java simples: FindAllClassesUsingPlainJavaReflectionTest.java

@Slf4j
class FindAllClassesUsingPlainJavaReflectionTest {

  private static final Function<Throwable, RuntimeException> asRuntimeException = throwable -> {
    log.error(throwable.getLocalizedMessage());
    return new RuntimeException(throwable);
  };

  private static final Function<String, Collection<Class<?>>> findAllPackageClasses = basePackageName -> {

    Locale locale = Locale.getDefault();
    Charset charset = StandardCharsets.UTF_8;
    val fileManager = ToolProvider.getSystemJavaCompiler()
                                  .getStandardFileManager(/* diagnosticListener */ null, locale, charset);

    StandardLocation location = StandardLocation.CLASS_PATH;
    JavaFileObject.Kind kind = JavaFileObject.Kind.CLASS;
    Set<JavaFileObject.Kind> kinds = Collections.singleton(kind);
    val javaFileObjects = Try.of(() -> fileManager.list(location, basePackageName, kinds, /* recurse */ true))
                             .getOrElseThrow(asRuntimeException);

    String pathToPackageAndClass = basePackageName.replace(".", File.separator);
    Function<String, String> mapToClassName = s -> {
      String prefix = Arrays.stream(s.split(pathToPackageAndClass))
                            .findFirst()
                            .orElse("");
      return s.replaceFirst(prefix, "")
              .replaceAll(File.separator, ".");
    };

    return StreamSupport.stream(javaFileObjects.spliterator(), /* parallel */ true)
                        .filter(javaFileObject -> javaFileObject.getKind().equals(kind))
                        .map(FileObject::getName)
                        .map(fileObjectName -> fileObjectName.replace(".class", ""))
                        .map(mapToClassName)
                        .map(className -> Try.of(() -> Class.forName(className))
                                             .getOrElseThrow(asRuntimeException))
                        .collect(Collectors.toList());
  };

  @Test
  @DisplayName("should get classes recursively in given package")
  void test() {
    Collection<Class<?>> classes = findAllPackageClasses.apply(getClass().getPackage().getName());
    assertThat(classes).hasSizeGreaterThan(4);
    classes.stream().map(String::valueOf).forEach(log::info);
  }
}

PS: para simplificar clichês para lidar com erros, etc, estou usando aqui vavre lombokbibliotecas

outras implementações podem ser encontradas no meu repositório GitHub daggerok / java-reflexão-encontrar-anotado-classes-ou métodos

Maksim Kostromin
fonte
0

Não consegui encontrar um trabalho curto para algo tão simples. Então aqui está, eu mesmo fiz isso depois de me meter um pouco:

    Reflections reflections =
        new Reflections(new ConfigurationBuilder()
                .filterInputsBy(new FilterBuilder().includePackage(packagePath))
                .setUrls(ClasspathHelper.forPackage(packagePath))
                .setScanners(new SubTypesScanner(false)));

    Set<String> typeList = reflections.getAllTypes(); 
botenvouwer
fonte
0

Se você estiver em Spring-land, poderá usar PathMatchingResourcePatternResolver;

  PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
  Resource[] resources = resolver.getResources("classpath*:some/package/name/*.class");

    Arrays.asList(resources).forEach(r->{
        ...
    });
Preto
fonte
-4

Não é possível, pois todas as classes no pacote podem não ser carregadas, enquanto você sempre conhece o pacote de uma classe.

Marko
fonte