Java扩展机制可加载所有JAR
Java擴展機制在Java教程中被描述為“一種標準的,可伸縮的方式,以使自定義API可供Java平臺上運行的所有應用程序使用?!?如了解擴展類加載中所述 ,“擴展框架利用類加載委托機制”,其中擴展類在rt.jar (和相關的JAR)中的引導類之后,但在從典型類路徑加載的類之前加載。
擴展目錄的工作方式與類路徑有點類似,因為擴展目錄的一部分是類加載機制的一部分,擴展目錄中JAR中可用的類對Java應用程序可用。 但是有一些關鍵的區別,下面將重點介紹其中一些。
通常針對特定應用
主機上可能所有的JRE
| 在特定JRE中運行的所有JVM
所有主持人的JRE
|
.jar文件
.class文件
| 加載指定目錄中的所有JAR文件(即使擴展名不是.jar或根本沒有擴展名)。 |
| 引導程序和擴展加載之后。 | 在引導程序之后但在類路徑之前。 |
最值得一提的最重要觀察結果之一是,即使擴展名機制沒有.jar擴展名,其擴展機制仍會提取擴展名目錄中的所有JAR格式文件。 這樣做的含義是,雖然可以將位于類路徑目錄中的JAR的名稱更改為.jar以外的擴展名,以使通配符不起作用,但該技術不適用于擴展名目錄。
我將在本文中使用一些簡單的示例來演示其中的一些差異。 接下來的兩個代碼清單是一個非常簡單的HelloWorld類和一個名為Main的主應用程序類,它使用HelloWorld類。
HelloWorld.java
public class HelloWorld {@Overridepublic String toString(){return "Hello, World!";} }Main.java
import static java.lang.System.out;public class Main {public static void main(final String[] arguments){out.println(new HelloWorld());} }為了演示類路徑和擴展機制( 可選軟件包 )之間的主要區別,我將把已編譯的HelloWorld.class文件歸檔到一個名為HelloWorld.jar的JAR中,并將其放置在與已編譯的Main.class文件不同的目錄中。
為了演示傳統類路徑的用法,我將HelloWorld.jar文件放置在名為C:\hello的目錄中,并將通過通配符(*)訪問該JAR,以便Main使用。 接下來的兩個屏幕快照對此進行了演示。
前面的兩個圖像表明,即使我已將Java Main應用程序從當前目錄中刪除,Java Main應用程序仍然可以加載HelloWorld.class文件,因為已明確告知Java啟動程序 (通過-classpath選項)在C:\hello查找它C:\hello 使用擴展機制(可選軟件包),可以加載類而無需將其放在同一目錄中并且沒有明確的類路徑規范。 這顯示在下一個屏幕快照中。
上一個屏幕快照展示了Java啟動器甚至不需要在同一目錄中或在其類路徑中指定的HelloWorld.class時,該類位于擴展(可選軟件包)目錄中的JAR中。 人們通常認為這是使用擴展機制的好處,因為使用該JRE的所有應用程序(或主機上可能的所有應用程序)都可以看到相同的類,而無需在類路徑中明確指定它們。
使用指示應用程序從JAR加載類的傳統類路徑方法,包含.class文件的JAR文件需要以.jar擴展名結尾。 下一個屏幕快照演示了在同一類路徑引用的目錄中將HelloWorld.jar重命名為HelloWorld.backup時發生的情況。
最后一張圖片演示了當類路徑引用目錄中的JAR文件沒有.jar擴展名時,遇到NoClassDefFoundError 。 可能有些令人驚訝的是,擴展(可選軟件包)機制的工作方式不同。 而是,將在擴展名指定目錄中加載所有JAR文件,而不管其擴展名如何,即使它們具有文件擴展名也是如此。 在下一個屏幕圖像中將對此進行演示。
上一張圖像演示了將擴展目錄中的JAR文件重命名為沒有任何文件擴展名不會阻止類加載器加載該JAR的類。 換句話說,類加載機制基于文件類型而不是文件名或擴展名來加載指定擴展目錄中的所有JAR文件。 正如Optional Packages Overview總結的那樣,“對于任何特定的JAR文件本身或其中包含的使其成為已安裝的可選軟件包的類,沒有什么特別的。 由于它位于jre / lib / ext中,因此它是一個已安裝的可選軟件包?!?
在擴展目錄內的JAR中放置太多類定義會帶來一些風險和弊端 。 例如,當人們看到在類路徑上顯式指定的類具有所涉及的方法時,為什么會發生NoSuchMethodError可能令人發狂 。 我之前已經寫過 NoSuchMethodError的許多潛在原因之一,但是擴展目錄中JAR文件中遺忘的過時和過時的類定義是另一個潛在原因。 接下來演示。
接下來的兩個代碼清單顯示Main.java和HelloWorld.java修訂版。 特別是, HelloWorld具有Main新版本調用的全新方法。 在這種情況下,當我運行Main時,我將把新編譯的HelloWorld.class文件保留在同一目錄中,以證明擴展目錄中JAR中的HelloWorld.class的舊版本優先于新版本。當前目錄中的熱點HelloWorld.class 。
修改后的Hello World.java(新方法)
public class HelloWorld {@Overridepublic String toString(){return "Hello, World!";}public String directedHello(final String name){return "Hello, " + name;} }修改后的Main.java
import static java.lang.System.out;public class Main {public static void main(final String[] arguments){final HelloWorld helloWorld = new HelloWorld();out.println(helloWorld);out.println(helloWorld.directedHello("Dustin"));} }
上一張圖片展示了extensions目錄中現在過時的HelloWorld類定義優先于同一目錄中的HelloWorld新類定義。 即使當我在類路徑上指定當前目錄時,擴展目錄中的舊版本也具有優先權。 這在下一個屏幕快照中顯示,該快照還顯示擴展目錄中“隱藏”較新JAR及其類的較新方法的JAR甚至都沒有以.jar擴展名命名。
剛剛演示的示例甚至都不是在指定的擴展目錄(或多個目錄)中忘記JAR可能導致的最困難的情況。 在那個例子中,至少我有一個NoSuchMethodError來提醒我一個問題。 當舊類定義具有相同的方法簽名但具有過時的方法實現時,可能存在調試甚至可能更加困難的情況。 在這種情況下,可能沒有任何錯誤,異?;蚩蓲伋?#xff0c;但是應用程序邏輯將無法正常工作或無法正常工作。 以前的功能可能在代碼庫中存在一段時間,甚至在被認為是問題之前,尤其是在缺少單元測試和其他測試的情況下。
使用extensions目錄可以使開發人員更輕松,因為存在于extensions目錄(或多個目錄)中的JAR文件中的類可用于與extensions目錄關聯的JRE中的所有應用程序(如果使用操作系統,還可以與主機上的所有JRE關聯)使用?;谥鳈C的擴展目錄。 但是,過于自由地使用目錄存在一定的風險。 很容易忘記,該目錄中JAR中的過時類定義正在阻止類加載器加載類定義的較新的,看似明顯的版本。 當發生這種情況時,使開發人員的生活變得更輕松的擴展(可選軟件包)機制現在變得更加困難。
Elliotte Rusty Harold提供了有關使用擴展(可選軟件包)機制的警告,“雖然這看起來很方便,但這也是一個長期錯誤……遲早(可能不久),您將加載錯誤版本的類從您根本沒有想過的地方開始,就浪費了很多時間進行調試?!?Java教程還建議您謹慎(我強調了這一點),“由于此機制擴展了平臺的核心API,因此應謹慎使用它 。 盡管它也可能適用于站點范圍的接口,但最通常用于標準化接口(如Java Community Process定義的接口)?!?
盡管擴展(可選軟件包)機制與類路徑機制相似,并且兩者均用作類加載的一部分,但要注意兩者之間的差異。 特別是,請務必記住,將加載位于引用為擴展目錄的目錄中的所有JAR文件(即使它們沒有.jar文件擴展名)。 重命名這些JAR甚至更改其文件擴展名都不足以使類加載忽略它們。 另一方面,對于classpath,重命名JAR足以防止在classpath顯式指定單個JAR文件時進行加載,并且即使classpath使用通配符(*)來指定所有JAR,更改文件擴展名通常也足以防止加載目錄。
在某些情況下,擴展(可選軟件包)機制是適當的選擇,但這些情況似乎很少見。 在處理無法解釋的NoSuchMethodError時,請記住擴展機制(可選軟件包),這一點也很重要,以便人們可以檢查出該違規者是否住在該目錄中。
翻譯自: https://www.javacodegeeks.com/2014/10/java-extension-mechanism-loads-all-jars.html
總結
以上是生活随笔為你收集整理的Java扩展机制可加载所有JAR的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 氨水是混合物吗 氨水是不是混合物
- 下一篇: 关于Java的十件事