簡體   English   中英

如何枚舉包中的所有類並將它們添加到List?

[英]How can I enumerate all classes in a package and add them to a List?

我需要枚舉包中的所有類並將它們添加到List中。 單個類的非動態版本如下:

List allClasses = new ArrayList();
allClasses.add(String.class);

如何動態地添加包中的所有類及其所有子包?


更新:在閱讀了早期答案之后,我試圖解決另一個次要問題是絕對正確的,所以讓我說明一下。 我知道這是可能的,因為其他工具可以做到這一點。 在這里查看新問題。

更新:再次閱讀本文,我可以看到它是如何被誤讀的。 我想在編譯后從文件系統中枚舉所有MY PROJECT'S類。

****更新1(2012年)****

好的,我終於開始清理下面的代碼片段了。 我堅持使用它自己的github項目甚至添加了測試。

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

****更新2(2016)****

有關更強大且功能豐富的類路徑掃描程序,請參閱https://github.com/lukehutch/fast-classpath-scanner/wiki 我建議先閱讀我的代碼片段以獲得高級別的理解,然后使用lukehutch的工具進行制作。

****原帖(2010)****

嚴格來說,無法在包中列出類。 這是因為包實際上只是一個命名空間(例如com.epicapplications.foo.bar),並且類路徑中的任何jar文件都可能將類添加到包中。 更糟糕的是,類加載器將按需加載類,而類路徑的一部分可能位於網絡連接的另一端。

可以解決更嚴格的問題。 例如,JAR文件中的所有類,或JAR文件在特定包中定義的所有類。 無論如何,這是更常見的情況。

不幸的是,沒有任何框架代碼可以輕松完成這項任務。 您必須以類似於ClassLoader查找類定義的方式掃描文件系統。

網上有很多樣本用於普通舊目錄中的類文件。 這些天我們大多數人都使用JAR文件。

要使用JAR文件,請嘗試這個...

private static ArrayList<Class<?>> getClassesForPackage(Package pkg) {
    String pkgname = pkg.getName();
    ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
    // Get a File object for the package
    File directory = null;
    String fullPath;
    String relPath = pkgname.replace('.', '/');
    System.out.println("ClassDiscovery: Package: " + pkgname + " becomes Path:" + relPath);
    URL resource = ClassLoader.getSystemClassLoader().getResource(relPath);
    System.out.println("ClassDiscovery: Resource = " + resource);
    if (resource == null) {
        throw new RuntimeException("No resource for " + relPath);
    }
    fullPath = resource.getFile();
    System.out.println("ClassDiscovery: FullPath = " + resource);

    try {
        directory = new File(resource.toURI());
    } catch (URISyntaxException e) {
        throw new RuntimeException(pkgname + " (" + resource + ") does not appear to be a valid URL / URI.  Strange, since we got it from the system...", e);
    } catch (IllegalArgumentException e) {
        directory = null;
    }
    System.out.println("ClassDiscovery: Directory = " + directory);

    if (directory != null && directory.exists()) {
        // Get the list of the files contained in the package
        String[] files = directory.list();
        for (int i = 0; i < files.length; i++) {
            // we are only interested in .class files
            if (files[i].endsWith(".class")) {
                // removes the .class extension
                String className = pkgname + '.' + files[i].substring(0, files[i].length() - 6);
                System.out.println("ClassDiscovery: className = " + className);
                try {
                    classes.add(Class.forName(className));
                } 
                catch (ClassNotFoundException e) {
                    throw new RuntimeException("ClassNotFoundException loading " + className);
                }
            }
        }
    }
    else {
        try {
            String jarPath = fullPath.replaceFirst("[.]jar[!].*", ".jar").replaceFirst("file:", "");
            JarFile jarFile = new JarFile(jarPath);         
            Enumeration<JarEntry> entries = jarFile.entries();
            while(entries.hasMoreElements()) {
                JarEntry entry = entries.nextElement();
                String entryName = entry.getName();
                if(entryName.startsWith(relPath) && entryName.length() > (relPath.length() + "/".length())) {
                    System.out.println("ClassDiscovery: JarEntry: " + entryName);
                    String className = entryName.replace('/', '.').replace('\\', '.').replace(".class", "");
                    System.out.println("ClassDiscovery: className = " + className);
                    try {
                        classes.add(Class.forName(className));
                    } 
                    catch (ClassNotFoundException e) {
                        throw new RuntimeException("ClassNotFoundException loading " + className);
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(pkgname + " (" + directory + ") does not appear to be a valid package", e);
        }
    }
    return classes;
}

我想出了如何做到這一點。 這是程序:

  1. 從根包中的類開始,從類加載器中獲取它所在的文件夾
  2. 遞歸枚舉此文件夾中的所有.class文件
  3. 將文件名轉換為完全限定的類名
  4. 使用Class.forName()來獲取類

這里有一些令人討厭的技巧讓我有點不安,但它有效 - 例如:

  1. 使用字符串操作將路徑名轉換為包名
  2. 對根軟件包名稱進行硬編碼以啟用剝離路徑前綴

太糟糕了,stackoverflow不允許我接受我自己的答案......

您可以嘗試我的庫FastClasspathScanner

要枚舉包中的所有類,請執行以下操作:

Set<String> classNames = new FastClassPathScanner("com.mypackage")
    .scan()
    .getNamesOfAllClasses();

我擔心你必須手動掃描類路徑和java搜索類的其他地方(例如,ext目錄或引導類路徑)。 由於java使用延遲加載類,因此它甚至可能不知道包中尚未加載的其他類。 還要檢查“密封”包裝的概念。

有趣的是,這個問題每隔一段時間就出現一次。 問題是這個關鍵字更適合命名為“namespace”。 Java包不會描述一個包含包中所有類的具體容器。 它只是定義了一個令牌,類可以用來聲明它們是該包的成員。 您必須搜索整個類路徑(如指示的另一個回復)以確定包中的所有類。

需要注意的是:像tomcat和JBoss這樣的ApplicationEngines / servlet容器具有分層類加載器 獲取系統類加載器是行不通的。

Tomcat的工作方式(事情可能已經發生變化,但我目前的經驗並沒有讓我相信)但是每個應用程序上下文都有自己的類加載器,因此應用程序'foo'的類不會與應用程序'fooV2的類沖突“

僅作為一個例子。 如果所有類都進入了一個超級類上下文,那么你就不知道你是否使用了適用於版本1或版本2的類。

此外,每個人都需要訪問系統類,如java.lang.String。 這是層次結構。 它首先檢查本地應用程序上下文並將其向上移動(這是我目前的情況BTW)。

要管理它,更好的方法是: this.getClass()。getClassloader()

就我而言,我有一個需要在某些模塊上進行自我發現的web服務,它們顯然位於“this”webservice上下文或系統上下文中。 通過以上操作,我可以檢查兩者。 通過獲取系統類加載器,我無法訪問任何應用程序類(因此我的資源為null)。

看看java.net.URLClassLoader正在做什么。 它從不枚舉類,它只是在被要求時嘗試查找類。 如果要枚舉類,則需要獲取類路徑,將其拆分為目錄和jar文件。 掃描目錄(及其子目錄)和jar文件,查找名稱為* .class的文件。

可能值得查看開源項目,這些項目似乎是您想要的枚舉(如Eclipse )的靈感。

如果您只是想加載一組相關的類,那么Spring可以幫助您。

Spring可以在一行代碼中實例化實現給定接口的所有類的列表或映射。 列表或映射將包含實現該接口的所有類的實例。

話雖這么說,作為從文件系統中加載類列表的替代方法,而不是在所有要加載的類中實現相同的接口,而不管包。 這樣,您可以加載(並實例化)您想要的所有類,而不管它們是什么包。

另一方面,如果將它們全部放在包中是您想要的,那么只需讓該包中的所有類實現給定的接口即可。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM