2010-07-11 17 views

risposta

44

È possibile ottenere tutte le radici del classpath passando uno String vuoto in ClassLoader#getResources().

Enumeration<URL> roots = classLoader.getResources(""); 

è possibile costruire un File sulla base di URL come segue:

File root = new File(url.getPath()); 

È possibile utilizzare File#listFiles() per ottenere un elenco di tutti i file nella directory data:

for (File file : root.listFiles()) { 
    // ... 
} 

Puoi utilizzare i metodi standard java.io.File per verificare se si tratta di una directory e/o per prendere il nome file.

if (file.isDirectory()) { 
    // Loop through its listFiles() recursively. 
} else { 
    String name = file.getName(); 
    // Check if it's a .class file or a .jar file and handle accordingly. 
} 

In base al solo requisito funzionale, immagino che il Reflections library è molto più esattamente quello che vuoi.

+0

Cosa si intende per "radici classpath "? Ho notato che 'classLoader.getResources (" ")' restituisce solo una o due directory sul classpath, quelle che sembrano contenere i file .class che ho scritto io stesso (e non quelli in pacchetti di terze parti). – flow2k

+0

E se volessimo l'elenco completo di classpath, non dovremmo usare 'URL [] urls = ((URLClassLoader) classLoader) .getURLs()'? – flow2k

0

Ogni tanto lo cerco. È un po 'difficile perché anche se riesci a trovare tutto sul classpath, potresti non trovare tutto disponibile per un determinato programma di caricamento classi (ad esempio, ho lavorato su un progetto che ha caricato le definizioni di classe direttamente da un DB una volta).

La migliore scommessa a questo punto è probabilmente quella di guardare in primavera. Esaminano le classi sul classpath per vedere se hanno delle annotazioni di cui hanno bisogno per fare il kickstart.

La risposta accettata: ecco un buon punto di partenza:

Scanning Java annotations at runtime

13

Ecco quello che ho scritto per farlo. Sono sicuro che non ottiene tutto se stai facendo qualcosa di strano con il classpath, ma sembra funzionare bene per me. Nota che in realtà non carica le classi, restituisce solo i loro nomi. Questo è così che esso non verrà caricato tutte le classi in memoria, e perché alcune classi in codice di base della mia azienda sono stati causando errori di inizializzazione se caricato al momento sbagliato ...

public interface Visitor<T> { 
    /** 
    * @return {@code true} if the algorithm should visit more results, 
    * {@code false} if it should terminate now. 
    */ 
    public boolean visit(T t); 
} 

public class ClassFinder { 
    public static void findClasses(Visitor<String> visitor) { 
     String classpath = System.getProperty("java.class.path"); 
     String[] paths = classpath.split(System.getProperty("path.separator")); 

     String javaHome = System.getProperty("java.home"); 
     File file = new File(javaHome + File.separator + "lib"); 
     if (file.exists()) { 
      findClasses(file, file, true, visitor); 
     } 

     for (String path : paths) { 
      file = new File(path); 
      if (file.exists()) { 
       findClasses(file, file, false, visitor); 
      } 
     } 
    } 

    private static boolean findClasses(File root, File file, boolean includeJars, Visitor<String> visitor) { 
     if (file.isDirectory()) { 
      for (File child : file.listFiles()) { 
       if (!findClasses(root, child, includeJars, visitor)) { 
        return false; 
       } 
      } 
     } else { 
      if (file.getName().toLowerCase().endsWith(".jar") && includeJars) { 
       JarFile jar = null; 
       try { 
        jar = new JarFile(file); 
       } catch (Exception ex) { 

       } 
       if (jar != null) { 
        Enumeration<JarEntry> entries = jar.entries(); 
        while (entries.hasMoreElements()) { 
         JarEntry entry = entries.nextElement(); 
         String name = entry.getName(); 
         int extIndex = name.lastIndexOf(".class"); 
         if (extIndex > 0) { 
          if (!visitor.visit(name.substring(0, extIndex).replace("/", "."))) { 
           return false; 
          } 
         } 
        } 
       } 
      } 
      else if (file.getName().toLowerCase().endsWith(".class")) { 
       if (!visitor.visit(createClassName(root, file))) { 
        return false; 
       } 
      } 
     } 

     return true; 
    } 

    private static String createClassName(File root, File file) { 
     StringBuffer sb = new StringBuffer(); 
     String fileName = file.getName(); 
     sb.append(fileName.substring(0, fileName.lastIndexOf(".class"))); 
     file = file.getParentFile(); 
     while (file != null && !file.equals(root)) { 
      sb.insert(0, '.').insert(0, file.getName()); 
      file = file.getParentFile(); 
     } 
     return sb.toString(); 
    } 
} 

Per utilizzarlo:

ClassFinder.findClasses(new Visitor<String>() { 
    @Override 
    public boolean visit(String clazz) { 
     System.out.println(clazz) 
     return true; // return false if you don't want to see any more classes 
    } 
}); 
+2

È necessario utilizzare String [] paths = classpath.split (System.getProperty ("path.separator")); altrimenti non funzionerà su Linux – sherif

+0

@sherif modificato, grazie! – Andy

+0

Mi piacerebbe provare. Puoi mostrare il codice per usare ClassFinder? –

7

È possibile utilizzare le classi di utilità dal pacchetto com.google.common.reflect dalla libreria Guava. Per esempio. per ottenere tutte le classi in un particolare pacchetto:

ClassLoader cl = getClass().getClassLoader(); 
    Set<ClassPath.ClassInfo> classesInPackage = ClassPath.from(cl).getTopLevelClassesRecursive("com.mycompany.mypackage"); 

Questo è concisa tuttavia descrivere, si applicano ancora gli stessi avvertimenti come altre risposte, cioè, che sarà generalmente solo trovare classi caricate da un 'standard' ClassLoader esempio URLClassLoader.

0

Suggerisco di utilizzare Spring's PathMatchingResourcePatternResolver;

Si farà il trucco sia per il lancio del pacchetto da un IDE o dal file system:

Per maggiori dettagli leggi la mia nota qui:

How to get resources from a package

Problemi correlati