簡體   English   中英

自定義類加載器在斜杠中使用名稱ENDING的getResources時出現問題

[英]Custom classloader trouble with getResources for names ENDING in slash

我迫不及待地尋求幫助,但無法在網上找到關於這個特定主題的任何內容(許多相關的問題都沒有得到解答)。

具體來說,我需要能夠從中央和外部代碼庫下載代碼(jar)。 這是由引導代碼完成的,需要將其添加到類加載器的類路徑中以便以后使用。 這是我們進入已經討論了很多次的主題。 我不喜歡黑客,所以我嘗試了以下方法:

嘗試#1:創建為此目的配置的URLClassLoader實例,然后通過它調用代碼的“rest”。

失敗:這里有1.5個問題(一個可能是另一個問題)。 一個是URLClassLoader通常更喜歡從其父級加載東西。 一些代碼必須存在於兩者中,可能存在不同的版本。 如果使用來自父代碼的代碼,它繼續使用“外部”類加載器進行剩余的加載,這不是我們想要的,即使初始加載是正常的。 其次,一些第三方庫似乎直接訪問系統類加載器,無論是設計還是意外(可以從其加載的類中獲取它)。

嘗試#2:創建URLClassLoader的子類,它更喜歡self而不是父級。 覆蓋loadClass,getResource,getResources,getPackage,getPackages ......以后的其他方法也可以確保這一點。

失敗:沒有幫助(足夠)。 該第三方代碼仍然無法加載某些資源

嘗試#3創建URLClassLoader的另一個自定義子類,並使用-Djava.system.class.loader = ...將其設置為系統類加載器

失敗:這樣做得更好 - 走得更遠,但仍未能嘗試獲取資源。 但這次是不同的資源。 我添加了日志記錄到所有重寫的方法來記錄他們的調用和資源名稱。 經常找到資源。 有些人仍然沒有,即使他們在那里(已確認)。 但是,即使我努力學習,我還不知道的是有很多關於資源名稱以斜線結尾的調用。 有些也有斜線,通常會出現美元符號(嵌套/內部類資源)。 一些已請求但未找到的示例:

com / acme / foo / bar / ClassName / com / acme / foo / bar / ClassName / InnerClassName /

當我在初始/ boot類路徑上運行所有內容的下載代碼(並且不使用我的類加載器)時,一切正常 - 因此我的類加載器會破壞一些東西,但我需要它才能工作。

我最接近的猜測是:

  1. 第三方代碼以某種方式獲取真正的系統類加載器,可能通過它加載的某個類,然后使用它。 我沒有看到它的請求,它們必然會失敗,因為它沒有整個類路徑。

  2. 資源名稱以斜杠結尾的業務是真正的系統類加載器支持的原因,而不是我子類化的URLClassLoader支持的原因。 我只能猜測,預期的返回URL會以某種方式定位具有該名稱作為前綴的資源集合。 雖然可能,但這很難匹配。 此外,似乎某些斜杠位於分隔內部類名稱的美元符號的位置,即在上面的示例中(為了清楚起見添加了空格):

com / acme / foo / bar / ClassName / InnerClassName /

com / acme / foo / bar / ClassName $ InnerClassName /

請注意,我不能依賴於破解實際的系統類加載器,假設它是URLClassLoader的子類並使用反射來調用其addURL(URL)方法。

有沒有辦法讓這項工作? 請幫忙!

UPDATE

我只是做了一個額外的嘗試。 我創建了一個虛擬包裝器類加載器(擴展ClassLoader,而不是URLClassLoader),它只記錄請求,然后將它們傳遞給父(公共方法)或超類(受保護的方法)。 我將其設置為系統類加載器並手動將整個“內部”類路徑添加到實際的外部路徑,然后嘗試運行代碼。 這是正常的,就像沒有自定義系統類加載器一樣。 記錄的內容還發現,即使是系統類加載器也會為這些資源返回null,這些資源以MOST結尾的斜杠結尾,但不是全部。 我沒有檢查這些是否也適用於我的真實代碼,但猜測它們可能 - 因為它們不是絆腳石。 不知何故,自定義系統類加載器仍然被繞過。 怎么樣?

更新2

在我的自定義系統類加載器中,我讓一些類來自外部/真正的系統類加載器,例如java.lang中的類。 我現在懷疑我不應該擁有內心的“世界”必須完全孤立。 但是,如果與它進行通信會產生問題,而我將留下的只是反思...但不確定這是否會起作用 - 即可以有多個java.lang.Class和/或java.lang 。賓語?

考慮到所有限制因素,這似乎並不像我想要的那樣以堅如磐石的方式完全實現:

a)第三方圖書館可能總是“行為不端”並抓住他們不應該以這種或那種方式使用的lassloader。 我按照fge的建議看了OneJar,但他們有同樣的問題 - 他們只檢測到它的可能性。

b)無法完全替換/隱藏系統類加載器。

c)將系統類加載器強制轉換為URLClassLoader可能隨時停止工作。

看來,你不了解類加載器結構。 在當前版本的普通JVM中,至少有三個類加載器:

  1. 引導加載程序 ,負責加載核心類。 因為這涉及類,如ClassClassLoader本身,它不能由一個表示ClassLoader實例。 getClassLoader()返回null所有類都由引導加載程序加載
  2. 擴展加載器。 它負責在JRE的ext/目錄中加載類。 Afaik,它可能會在未來的版本中消失。 它的父加載器是引導加載程序
  3. 應用程序加載器。 這是將由ClassLoader.getSystemClassLoader()返回的那個, 如果沒有指定其他父級 ,將使用它。 在當前配置中,它的父級是擴展加載器,但也許它將在未來版本中將引導加載程序作為其直接父級

結論是,如果要重新加載應用程序的類而不委托父加載器破壞您的工作,則不需要操作類加載器的實現。 您只需指定正確的父級。 這很簡單

URLClassLoader cl=new URLClassLoader(urls, ClassLoader.getSystemClassLoader().getParent());

這樣,新的類加載器的父級將是原始應用程序類加載器的父級,因此已加載的應用程序類不在新加載器的范圍內,而其他所有內容都像往常一樣工作。

關於以斜杠結尾的資源,它們並不常見。 它們實際引用目錄時可能會得到解決,但這取決於URL的協議和該協議的實際處理程序。 例如,它可能適用於file: URL但通常不適用於jar: URL,除非jar文件包含目錄的偽條目。 我也看到它適用於ftp: URL。

另一件需要理解的是,如果一個類直接引用另一個類,則將查詢其原始定義類加載器,而不一定是應用程序類加載器。 例如,當java.lang.String類包含對java.lang.Object (它確實)的引用時,將使用引導加載程序直接解析此引用,因為這是java.lang.String的定義加載器。

這意味着如果您操縱加載器的父查找不遵循標准父委派,則您可能會將名稱解析為不同的運行時類,因為在由父加載器加載的類引用時會解析相同的名稱。 您可以按照上述解決方案中的標准程序避免此類問題。 JRE類永遠不會包含對應用程序類的引用,並且新的加載器沒有原始的應用程序加載器,因為它的父級將永遠不會干擾原始應用程序加載器加載的類。

暫無
暫無

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

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