是否能夠在給定的包中找到全部類或接口? (快速查看例如Package
,彷佛沒有。) html
我整理了一個簡單的github項目來解決這個問題: java
https://github.com/ddopson/java-class-enumerator git
它應同時適用於基於文件的類路徑和jar文件。 github
若是您在簽出項目後運行「 make」,它將打印出來: apache
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.
另請參閱個人其餘答案 api
Google Guava 14包含一個新的ClassPath
類,該類具備三種方法來掃描頂級類: oracle
getTopLevelClasses()
getTopLevelClasses(String packageName)
getTopLevelClassesRecursive(String packageName)
有關更多信息,請參見ClassPath
javadocs 。 ui
一般,類加載器不容許掃描類路徑上的全部類。 可是一般惟一使用的類加載器是UrlClassLoader,咱們能夠從中檢索目錄和jar文件的列表(請參閱getURLs ),並一一打開它們以列出可用的類。 這種方法稱爲類路徑掃描,是在Scannotation and Reflections中實現的 。 google
Reflections reflections = new Reflections("my.package"); Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);
另外一種方法是使用Java Pluggable Annotation Processing API編寫註釋處理器,該處理器將在編譯時收集全部帶註釋的類並構建索引文件以供運行時使用。 此機制在ClassIndex庫中實現: url
// package-info.java @IndexSubclasses package my.package; // your code Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");
請注意,因爲Java編譯器自動發現了在類路徑上找到的任何處理器,所以掃描是徹底自動化的,所以不須要其餘設置。
您須要在類路徑中查找每一個類加載器條目:
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()); // .... }
若是條目是目錄,則只需在右側的子目錄中查找:
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)); } }
若是條目是文件,而且是jar,請檢查其ZIP條目:
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()); } }
如今,一旦有了全部帶有包的類名,就能夠嘗試使用反射加載它們,並分析它們是類仍是接口等。
我一直在嘗試使用Reflections庫,可是在使用它時遇到了一些問題,我應該包括的jar太多了,只是爲了簡單地獲取包中的類。
我將發佈在這個重複的問題中找到的解決方案: 如何獲取包中的全部類名稱?
答案是由sp00m編寫的 ; 我添加了一些更正使其生效:
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; } }
要使用它,只需像本例中提到的sp00n那樣調用find方法:若是須要,我添加了類實例的建立。
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(); //... }