Riesci a trovare tutte le classi in un pacchetto usando reflection?


528

È possibile trovare tutte le classi o interfacce in un determinato pacchetto? (Guardando rapidamente ad esempio Package, sembrerebbe di no.)


2
Cordiali saluti, la soluzione tra cui Amit si collega a funziona, sebbene abbia un bug se il percorso della classe ha un carattere spazio (e probabilmente anche per altri caratteri non alfanumerici). se lo stai usando in qualsiasi tipo di codice di produzione, vedi il mio commento alla sua risposta per una soluzione alternativa.
Kip,

2
Nota anche questo post .
Barfuin,

1
Si veda la risposta correlato: stackoverflow.com/a/30149061/4102160
Cfx

1
Nota anche questo post .
sp00m,

1
Vedi la mia risposta di seguito su ClassGraph, attualmente è il metodo più affidabile per scansionare il percorso di classe e il percorso del modulo.
Luke Hutchison,

Risposte:


373

A causa della natura dinamica dei caricatori di classi, ciò non è possibile. I programmi di caricamento classi non sono tenuti a comunicare alla VM quali classi è in grado di fornire, invece sono solo richieste passate per le classi e devono restituire una classe o generare un'eccezione.

Tuttavia, se si scrivono i propri caricatori di classi o si esaminano i percorsi di classe e i relativi vasi, è possibile trovare queste informazioni. Questo avverrà tramite operazioni sul filesystem e non riflessioni. Potrebbero anche esserci delle librerie che possono aiutarti a farlo.

Se ci sono classi che vengono generate o distribuite in remoto, non sarai in grado di scoprire quelle classi.

Il metodo normale è invece di registrare da qualche parte le classi a cui è necessario accedere in un file o fare riferimento a loro in una classe diversa. O semplicemente usa la convenzione quando si tratta di nominare.

Addendum: la Reflections Library ti permetterà di cercare le classi nel percorso di classe corrente. Può essere usato per ottenere tutte le classi in un pacchetto:

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

 Set<Class<? extends Object>> allClasses = 
     reflections.getSubTypesOf(Object.class);

12
L'incapacità di interrogare i nomi delle classi mi ha infastidito per molto tempo. Certo, è difficile e le prestazioni possono variare ampiamente, e per alcuni classloader l'elenco è indefinito o illimitato, ma ci sono modi per aggirare il problema.
Mr. Shiny e New

16
Si noti che questa soluzione non funzionerà poiché per impostazione predefinita getSubTypesOf non restituisce i sottotipi di Object. Vedi la soluzione di Aleksander Blomskøld per come configurare SubTypeScanner.
Alex Spurling,

17
Le riflessioni richiedono guava. La guava è grande. La versione 14.0.1 è 2.1 MB.
mike jones,

3
Non ha funzionato per me. Mac OSX - Versione di dipendenza riflessioni 0.9.9-RC1 (maven) - JDK 1.7. Riconsidera la risposta accettata. @ AleksanderBlomskøld la risposta è quella da fare. !!!!!
Konstantinos Margaritis,

68
Se restituisce un elenco vuoto, inizializza l'oggetto Reflections in questo modo: Reflections reflections = new Reflections ("your.package.here", nuovo SubTypesScanner (false));
João Rocha da Silva,

186

Probabilmente dovresti dare un'occhiata alla libreria di riflessioni open source . Con esso puoi facilmente ottenere quello che vuoi.

Innanzitutto, imposta l'indice delle riflessioni (è un po 'disordinato poiché la ricerca di tutte le classi è disabilitata per impostazione predefinita):

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"))));

Quindi è possibile eseguire una query per tutti gli oggetti in un determinato pacchetto:

Set<Class<?>> classes = reflections.getSubTypesOf(Object.class);

6
Ah, eccoci qui: code.google.com/p/reflections/issues/detail?id=122 . L'oggetto è escluso per impostazione predefinita, ma è possibile riattivarlo. Grazie per avermi indicato questa libreria, è fantastico!
mtrc,

1
Mi sono imbattuto in problemi sul mio Mac con questo codice (relativo alle librerie native), ma l'utilizzo al .addUrls(ClasspathHelper.forJavaClassPath())posto di quanto sopra li ha risolti per me. Anche meno codice!
David Pärsson,

3
se qualcuno si chiede che il modo più semplice per ottenere il pacchetto predefinito sia avere il prefisso come stringa vuota -> "".
JBA,

2
La libreria "Reflections" ha una licenza complicata: github.com/ronmamo/reflections/blob/master/COPYING.txt . Il trucco è che la licenza consente l'uso gratuito solo della licenza stessa. Quindi, per utilizzare davvero la libreria (non la licenza) tutti devono contattare l'autore e negoziare i termini di utilizzo.
Serge Rogatch,

3
Serge, penso che tu fraintenda WTFPL: wtfpl.net Penso che WTFPL significhi che sei libero di fare tutto quello che vuoi, non solo con la licenza ma anche con il codice
Richo,

122

Google Guava 14 include una nuova classe ClassPathcon tre metodi per cercare classi di livello superiore:

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

Vedi i ClassPathjavadocs per maggiori informazioni.


questo ha funzionato per me dove Reflection non poteva fare (nessun antenato diretto comune, nessuna annotazione comune)
Riccardo Cossu,

1
Come ho già detto in un commento qui sotto , ClassPathè taggato con @Beta, quindi potrebbe non essere una buona idea per alcuni ...
Christian

1
Dire che funziona dove la riflessione non lo fa è un po 'strano, la soluzione è senza dubbio implementata usando la funzionalità reflection (e caricatore di classi).
Maarten Bodewes,

5
Penso che intendesse la biblioteca di Reflections menzionata nell'altra risposta.
Christoph Leiter,

Funziona con Java 11, se si utilizza guava versione 28.1-jre.
Gorjanz,

112

È possibile utilizzare questo metodo 1 che utilizza il 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 Questo metodo è stato preso originariamente da http://snippets.dzone.com/posts/show/4831 , che è stato archiviato da Internet Archive, come linkato ora. Lo snippet è disponibile anche su https://dzone.com/articles/get-all-classes-within-package .


6
Ho avuto un problema con questo se il mio percorso includeva spazi. La classe URL stava sfuggendo agli spazi %20, ma il new File()costruttore lo ha trattato come un valore letterale in percentuale di due zero. L'ho risolto cambiando la dirs.add(...)linea in questo: dirs.add(new File(resource.toURI())); ciò significava anche che dovevo aggiungere URISyntaxExceptionla clausola dei tiri digetClasses
Kip,

19
Hai appena copiato da dzone.com/articles/get-all-classes-within-package ! fare riferimento alla fonte la prossima volta
RS

19
+1 perché questa soluzione NON richiede librerie esterne ... MAI, davvero MAI accoppiare il tuo codice casualmente con le librerie solo per ottenere una piccola cosa come questa. sai che stai aggiungendo una potenziale superficie di attacco per gli attaccanti? Nov 2015 è stato scoperto un problema di Apache Commons che porta all'esecuzione di comandi remoti semplicemente avendo Apache Commons nel percorso di classe di un'app distribuita su Jboss / Weblogic [ foxglovesecurity.com/2015/11/06/…
sc0p

@Qix ha osservato correttamente che questo codice non supporta jar. Per supportare barattoli e directory . Il codice è stato modificato come indicato di seguito:
user1523177

1
Buona soluzione, ma sembra essere migliore se 'Class.forName (String className)' verrà sostituito con 'Class.forName (String className, inizializzazione booleana, caricatore ClassLoader)' dove 'initialize = false;' per non creare istanze di classe.
Andrei_N,

99

Primavera

Questo esempio è per Spring 4, ma è possibile trovare lo scanner classpath anche nelle versioni precedenti.

// 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: nella versione 14, l'API è ancora contrassegnata come @Beta , quindi fai attenzione nel codice di produzione.

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

5
Risposta eccellente. Ci sono troppe soluzioni qui che sono verbose, non testate, non funzionanti! Questo è fantastico: è conciso e testato (è di Guava). Molto bene! È utile, merita più voti.
JeanValjean,

Sfortunatamente, la ClassPathclasse in Guava è anche contrassegnata con @Beta: "Le API contrassegnate con l'annotazione @Beta a livello di classe o metodo sono soggette a modifiche. Possono essere modificate in qualsiasi modo o addirittura rimosse in qualsiasi versione principale. Se il tuo codice è una libreria stessa (ovvero viene utilizzata sul CLASSPATH degli utenti al di fuori del tuo controllo), non devi utilizzare le API beta, a meno che non li riconfezioni ... " code.google.com/p/guava-libraries/#Important_Warnings
Christian,

@Christian Ottimo punto, non me ne sono accorto! Grazie. Aggiungerò un'altra risposta usando lo scanner Spring Path, che sicuramente non è beta.
voho,

Per trovare classi statiche nidificate utilizzando la soluzione guava, getAllClasses()è possibile utilizzare il metodo.
v.ladynev,

1
La soluzione Spring è l'unica che funziona se eseguita da un vaso eseguibile.
Luca,

38

Ciao. Ho sempre avuto dei problemi con le soluzioni sopra (e su altri siti).
Come sviluppatore, sto programmando un componente aggiuntivo per un'API. L'API impedisce l'uso di librerie esterne o strumenti di terze parti. L'installazione consiste anche in una combinazione di codice in file jar o zip e file di classe che si trovano direttamente in alcune directory. Quindi il mio codice doveva essere in grado di funzionare in qualsiasi configurazione. Dopo molte ricerche ho escogitato un metodo che funzionerà in almeno il 95% di tutte le configurazioni possibili.

Il seguente codice è sostanzialmente il metodo overkill che funzionerà sempre.

Il codice:

Questo codice analizza un determinato pacchetto per tutte le classi incluse in esso. Funzionerà solo per tutte le classi in corso 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;
}

Questi tre metodi ti offrono la possibilità di trovare tutte le classi in un determinato pacchetto.
Lo usi in questo modo:

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

La spiegazione:

Il metodo prima ottiene la corrente ClassLoader. Recupera quindi tutte le risorse che contengono detto pacchetto e itera questi URLs. Quindi crea un URLConnectione determina quale tipo di URl abbiamo. Può essere una directory ( FileURLConnection) o una directory all'interno di un file jar o zip ( JarURLConnection). A seconda del tipo di connessione, verranno chiamati due metodi diversi.

Per prima cosa vediamo cosa succede se è un FileURLConnection.
Controlla innanzitutto se il file passato esiste ed è una directory. In tal caso, controlla se si tratta di un file di classe. In tal caso Class, verrà creato e inserito un oggetto ArrayList. Se non è un file di classe ma è una directory, semplicemente eseguiamo l'iterazione e facciamo la stessa cosa. Tutti gli altri casi / file verranno ignorati.

Se URLConnectionè un JarURLConnection, verrà chiamato l'altro metodo di supporto privato. Questo metodo scorre su tutte le voci nell'archivio zip / jar. Se una voce è un file di classe ed è all'interno del pacchetto Class, verrà creato e archiviato un oggetto nel file ArrayList.

Dopo che tutte le risorse sono state analizzate (il metodo principale) restituisce il ArrayListcontenimento di tutte le classi in un determinato pacchetto, che la corrente ClassLoaderconosce.

Se il processo fallisce in qualsiasi momento ClassNotFoundExceptionverrà lanciato un conteggio di informazioni dettagliate sulla causa esatta.


4
Questo esempio sembra richiedere l'importazione sun.net.www.protocol.file.FileURLConnection, che genera un avviso in fase di compilazione ("warning: sun.net.www.protocol.file.FileURLConnection è un'API proprietaria di Sun e potrebbe essere rimosso in una versione futura"). Esiste un'alternativa all'utilizzo di quella classe o è possibile sopprimere l'avviso utilizzando le annotazioni?
Christian

Questo metodo non funziona per le classi bootstrap, come quelle in java.lang, java.util, ... Queste possono essere trovate ottenendo System.getProperty ("sun.boot.class.path"), suddividendo con: o; (a seconda del sistema operativo), quindi eseguendo versioni leggermente modificate della suddetta checkDirectory e checkJarFile.
coderforlife,

1
È possibile aggirare l'avvertimento / errore utilizzando connection.getClass (). GetCanonicalName (). Equals ("sun.net.www.protocol.file.FileURLConnection"). Se lo desideri, puoi creare una URLConnection che ritieni DOVREBBE utilizzare sun.net.www.protocol.file.FileURLConnection e anche confrontare il nome della classe di connessione con il nome della classe che hai creato. Se sono entrambi uguali, puoi trattarlo come un'istanza di sun.net.www.protocol.file.FileURLConnection anziché fallire nel caso in cui il nome della classe cambi.
William decide il

1
@Christian Puoi evitare che FileURLConnection faccia qualcosa del genere: if ( ... instanceof JarURLConnecton) { ... } else { // Asume that the Connection is valid and points to a File }è quello che ho fatto sul mio codice per cercare le classi annotate dell'APP
Zardoz89,

15

Senza usare librerie 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;
    }
}

Quando eseguo questo in un JAR, upackageè null... :(
Christian

Per un pacchetto "com.mycompany.beans", sostituisci "test" con "com / mycompany / beans"
James Jithin

4
Ottengo un null quando utilizzo questo codice. Sembra funzionare solo se il tuo vaso è un eseguibile
Alao,

se hai ottenuto il nome del pacchetto da String pack = getPackage().getName();, devi aggiungerepack = pack.replaceAll("[.]", "/");
user2682877

13

In generale, i caricatori di classi non consentono la scansione di tutte le classi sul percorso di classe. Ma di solito l'unico caricatore di classi utilizzato è UrlClassLoader dal quale è possibile recuperare l'elenco di directory e file jar (vedere getURLs ) e aprirli uno per uno per elencare le classi disponibili. Questo approccio, chiamato scansione del percorso di classe, è implementato in Scannotation e Reflections .

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

Un altro approccio consiste nell'utilizzare l' API Java Pluggable Processing Annotation per scrivere un processore di annotazioni che raccoglierà tutte le classi annotate al momento della compilazione e creerà il file indice per l'uso in fase di runtime. Questo meccanismo è implementato nella libreria ClassIndex :

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

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

Si noti che non è necessaria alcuna installazione aggiuntiva poiché la scansione è completamente automatizzata grazie al compilatore Java che rileva automaticamente tutti i processori trovati sul percorso di classe.


questo scopre le classi confezionate in un vaso? Non sembra funzionare per me.
chiede il

quale strumento stai cercando di usare?
Sławek,

Sto usando la libreria Reflections. Ma ho funzionato dopo aver seguito la soluzione menzionata da @Aleksander Blomskøld per le versioni recenti di questa libreria.
chiede il

Ciao, sto usando eclipse e non riesco a farlo funzionare, ClassIndex.getPackageClasses ("my.package") restituisce una mappa vuota
Juan,

11

Il meccanismo più affidabile per elencare tutte le classi in un determinato pacchetto è attualmente ClassGraph , poiché gestisce la più ampia gamma possibile di meccanismi di specifica del percorso di classe , incluso il nuovo sistema di moduli JPMS. (Sono l'autore.)

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

5

Ecco come lo faccio. Eseguo la scansione di tutte le sottocartelle (sotto-pacchetti) e non provo a caricare classi anonime:

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

4

Ho messo insieme un semplice progetto github che risolve questo problema:

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

Dovrebbe funzionare per ENTRAMBE i percorsi di classe basati su file E per i file jar.

Se esegui 'make' dopo aver verificato il progetto, questo verrà stampato:

 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. 

Vedi anche la mia altra risposta


4

Sì, usando poche API che puoi, ecco come mi piace farlo, affrontato questo problema che stavo usando il core di ibernazione e ho dovuto trovare classi che erano annotate con una certa annotazione.

Trasformali in un'annotazione personalizzata con la quale contrassegnerai le classi che desideri selezionare.

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 {

}

Quindi segna la tua classe con esso come

@EntityToBeScanned 
public MyClass{

}

Crea questa classe di utilità che ha il seguente metodo

public class ClassScanner {

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

}

Chiamare il metodo allFoundClassesAnnotatedWithEntityToBeScanned () per trovare un set di classi.

Avrai bisogno delle librerie fornite di seguito

<!-- 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>

3

È necessario cercare ogni voce del caricatore di classi nel percorso di 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 la voce è directory, basta cercare nella sottodirectory corretta:

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 la voce è il file ed è jar, ispezionare le voci ZIP di esso:

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

Ora, una volta che hai tutti i nomi di classe nel pacchetto, puoi provare a caricarli con reflection e analizzare se sono classi o interfacce, ecc.


Cosa inseriresti per un pacchetto in un file Jar?
Kyle Bridenstine,

Questo esempio non passerà attraverso i sotto-pacchetti. Forse questo è interessante per alcuni ... @ mr-tea Basta specificare il pacchetto che stai cercando. Ho inserito questo in un progetto, specificato un pacchetto di test all'interno di quel progetto, compilato e impacchettato e chiamato l'esempio dal metodo principale del JAR. Ha funzionato come un fascino. :)
Christian,

3

Ho provato a usare la libreria Reflections, ma ho avuto dei problemi nell'usarlo, e c'erano troppi barattoli che dovrei includere solo per ottenere semplicemente le classi su un pacchetto.

Pubblicherò una soluzione che ho trovato in questa duplice domanda: come ottenere tutti i nomi delle classi in un pacchetto?

La risposta è stata scritta da sp00m ; Ho aggiunto alcune correzioni per farlo funzionare:

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

}

Per usarlo basta chiamare il metodo find come sp00n menzionato in questo esempio: se necessario, ho aggiunto la creazione di istanze delle classi.

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

3

Ho appena scritto una classe util, include metodi di prova, puoi avere un controllo ~

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

3

La soluzione di Aleksander Blomskøld non ha funzionato per me per i test parametrici @RunWith(Parameterized.class)durante l'utilizzo di Maven. I test sono stati nominati correttamente e anche dove sono stati trovati ma non eseguiti:

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

Un problema simile è stato segnalato qui .

Nel mio caso @Parameters sta creando istanze di ogni classe in un pacchetto. I test hanno funzionato bene se eseguiti localmente nell'IDE. Tuttavia, durante l'esecuzione di Maven non sono state trovate classi con la soluzione di Aleksander Blomskøld.

L'ho fatto funzionare con il seguente frammento ispirato al commento di David Pärsson sulla risposta di 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);

3

Che dire di questo:

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

È quindi possibile sovraccaricare la funzione:

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

Se è necessario testarlo:

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 il tuo IDE non ha un supporto per l'importazione:

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;

Funziona:

  • dal tuo IDE

  • per un file JAR

  • senza dipendenze esterne


2

Quasi tutte le risposte utilizzano Reflectionso leggono i file di classe dal file system. Se si tenta di leggere le classi dal file system, è possibile che si verifichino errori durante il pacchetto dell'applicazione come JAR o altro. Inoltre, potresti non voler utilizzare una libreria separata a tale scopo.

Ecco un altro approccio che è Java puro e non dipende dal file system.

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

Java 8 non è un must . È possibile utilizzare per i loop anziché i flussi. E puoi provarlo in questo modo

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

1
Non è molto utile a causa di: è necessario disporre di JDK da utilizzare ToolProvider.getSystemJavaCompiler(), questo codice non esegue la scansione dei pacchetti nidificati.
v.ladynev,

Non riesco a farlo funzionare con un pacchetto di un vaso esterno
Enrico Giurin,

1

Se non si utilizzano caricatori di classi dinamici, è possibile cercare il percorso di classe e per ogni voce cercare la directory o il file JAR.


1

Vale la pena citare

Se si desidera avere un elenco di tutte le classi in alcuni pacchetti, è possibile utilizzare Reflectionil seguente modo:

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

Questo creerà un elenco di classi che in seguito potrai usarle come desideri.


1

È molto possibile, ma senza librerie aggiuntive come Reflectionsè difficile ...
È difficile perché non hai lo strumento completo per ottenere il nome della classe.
E prendo il codice della mia 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;
}
}

0

Basato sulla risposta di @ Staale , e nel tentativo di non fare affidamento su librerie di terze parti, implementerei l'approccio del file system ispezionando la posizione fisica del primo pacchetto con:

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

0

Se stai semplicemente cercando di caricare un gruppo di classi correlate, Spring può aiutarti.

Spring può creare un'istanza di un elenco o di una mappa di tutte le classi che implementano una determinata interfaccia in una riga di codice. L'elenco o la mappa conterrà istanze di tutte le classi che implementano tale interfaccia.

Detto questo, in alternativa al caricamento dell'elenco di classi dal file system, implementa invece la stessa interfaccia in tutte le classi che desideri caricare, indipendentemente dal pacchetto e usa Spring per fornirti le istanze di tutte. In questo modo, puoi caricare (e istanziare) tutte le classi che desideri indipendentemente dal pacchetto in cui si trovano.

D'altra parte, se averli tutti in un pacchetto è quello che vuoi, allora semplicemente tutte le classi in quel pacchetto implementano una data interfaccia.


0

java semplice: 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: per semplificare le piastre di caldaia per la gestione degli errori, ecc., Sto usando qui vavre le lomboklibrerie

altre implementazioni sono state trovate nel mio repository GitHub daggerok / java-reflection-find-annotated-classi-or-method


0

Non riuscivo a trovare un lavoro corto tagliato per qualcosa di così semplice. Quindi eccolo qui, l'ho fatto io stesso dopo aver fatto un casino per un po ':

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

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

0

Se sei nella terra di primavera puoi usare PathMatchingResourcePatternResolver;

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

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

-4

Non è possibile, poiché tutte le classi nel pacchetto potrebbero non essere caricate, mentre si conosce sempre il pacchetto di una classe.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.