翻譯原文連接html
Java平臺旨在提供健壯,安全和可擴展的功能,以支持代碼和數據的移動性。Java虛擬機(JVM)中的Java ClassLoader是實現這些目標的關鍵組件。java
JVM負責在Java平臺上加載和執行代碼。它使用ClassLoader將Java類加載到Java運行時環境中。ClassLoader的架構使得在啓動時JVM不須要知道將在運行時加載的類的任何信息。幾乎全部基於Java的容器(如EJB或servlet容器)都實現了自定義ClassLoader,以支持熱部署和運行時平臺可擴展性等功能。 在實現此類基於Java的容器時,深刻了解ClassLoaders對於開發人員很是重要。算法
對於開發部署在這些容器上的組件的企業開發人員,這些知識將幫助您瞭解容器的工做方式以及調試問題。 本文介紹了Java ClassLoader體系結構,並討論了ClassLoaders對平臺安全性和可擴展性的影響,以及實現用戶定義的ClassLoader的方法。緩存
由ClassLoader加載的最小執行單元是Java類文件。類文件包含Java類的二進制表示形式,該類具備可執行字節碼和對該類使用的其餘類的引用,包括對Java API中的類的引用。換句話說,ClassLoader定位須要加載的Java類的字節碼,讀取字節碼,並建立java.lang.Class類的實例。以便JVM執行。當JVM啓動時,不會加載任何內容。會首先加載正在執行的程序的類文件,而後加載其餘正在執行的字節碼中被引用的類和接口。所以,JVM表現出延遲加載特性,即僅在須要時加載類,在啓動時,JVM不須要知道在運行時期間將加載的類。延遲加載在爲Java平臺提供動態可擴展性方面起着關鍵做用。經過在程序中實現自定義ClassLoader,能夠自定義Java ClassLoader。安全
Java 運行時中存在多個ClassLoaders實例,每一個實例都從不一樣的代碼庫加載類。例如,Java核心API類由引導程序(或原始)ClassLoader加載。特定應用程序類由系統(或應用程序)ClassLoader加載。另外,應用程序能夠定義本身的ClassLoader以從自定義庫加載代碼。Java 定義了ClassLoaders之間的父子關係。除引導程序ClassLoader以外的每一個ClassLoader都有一個父類ClassLoader,從概念上造成了ClassLoader的樹狀結構。引導程序ClassLoader是此樹的根,所以沒有父級。這種關係如圖所示。 bash
如下是當客戶端請求加載類時由ClassLoader執行的高級類加載算法:服務器
除了引導程序ClassLoader,它是在JVM中的本機代碼中實現的外,其餘ClassLoader都是經過擴展java.lang.Class.Loader類來實現的。如下代碼顯示了Java 2 ClassLoader API的相關方法:架構
public abstract class ClassLoader extends Object {
protected ClassLoader(ClassLoader parent) {
}
protected final Class defineClass( String name,byte[] b,int off,int len) throws ClassFormatError{
}
protected Class findClass(String className) throws ClassNotFoundException {
}
public Class loadClass(String className) throws ClassNotFoundException {
}
}
複製代碼
根據父委派模型,每一個ClassLoader在建立時都會分配一個父級。客戶端在ClassLoader的實例上調用loadClass方法來加載類。這啓動了前面解釋的類加載算法。在Java 2以前,java.lang.ClassLoader類中的loadClass方法被聲明爲abstract,在擴展java.lang.ClassLoader類時須要自定義ClassLoaders來實現它。實現loadClass方法至關複雜,所以在Java 2中已經改變了。隨着ClassLoader父委託模型的引入,java.lang.ClassLoader有一個loadClass方法的實現,它本質上是一個執行類加載的模板方法算法。 loadClass方法在類加載算法的第3步中調用findClass方法(在Java 2中引入)。自定義類加載器應該重寫此方法,以提供定位和加載Java類的自定義方法。這極大地簡化了自定義ClassLoader的實現。ide
findClass方法調用loadFromCustomRepository來搜索存儲庫中的給定類,若是找到,則讀取並返回該類的字節碼。該類的原始字節碼被傳遞到java.lang.ClassLoader類中實現的defineClass方法,該類返回java.lang.Class對象的實例。 這使得新類可用於正在運行的Java程序。defineClass方法還確保自定義ClassLoader不會經過從自定義存儲庫加載來從新定義核心Java API類。 若是傳遞給defineClass的類名以「java」開頭,則拋出SecurityException。加密
應該注意的是,在啓動時,JVM不須要知道傳遞給loadClass方法的字符串所表明的類。
Java 2委派模型並不是適用於全部狀況。在某些狀況下,ClassLoader必須與Java 2模型不一樣。例如,servlet規範建議,實現Web應用程序ClassLoader,以便包含在Web應用程序歸檔中的類和資源優先於駐留在容器範圍的JAR文件中的類和資源。爲了知足此建議,Web應用程序ClassLoader應首先在其本地存儲庫中搜索類和資源,而後再委託父類ClassLoader,從而偏離Java 2委派模型。此建議使Web應用程序可使用不一樣於servlet容器使用的類/資源版本。例如,可使用較新版本的XML解析器中提供的功能而不是servlet容器使用的功能來實現Web應用程序。
能夠經過覆蓋java.lang.Classloader類的loadClass方法來實現知足servlet規範建議的Web應用程序ClassLoader。
ClassLoaders提供了一些可在Java程序中使用的強大功能。
在正在運行的應用程序中升級軟件而不從新啓動它稱爲熱部署。對於Java應用程序,熱部署意味着在運行時升級Java類。ClassLoaders在基於Java的應用程序服務器中發揮重要做用,以實現熱部署。大多數基於Java的應用程序服務器(如EJB服務器和servlet容器)都使用此功能。ClassLoader沒法從新加載已加載的類,但使用ClassLoader的新實例會將類從新加載到正在運行的程序中。
ClassLoader customLoader = new CustomClassLoader(repository);
loadAndInvoke(customLoader,classToLoad);
System.out.println("waiting.Hit Enter to continue");
System.in.read();
customLoader = new CustomClassLoader(repository);
loadAndInvoke(customLoader,classToLoad);
複製代碼
建立CustomClassLoader的實例以從指定爲命令行參數的存儲庫加載類。loadAndInvoke加載一個類HelloWorld,它也被指定爲命令行參數,並在其實例上調用一個方法,該方法在控制檯上輸出一條消息。當程序在第6行等待用戶輸入時,能夠更改HelloWorld類(經過更改在控制檯上打印的消息)並從新編譯。 當程序繼續執行時,在第7行建立一個新的CustomClassLoader實例。當loadAndInvoke執行第9行時,它會加載HelloWorld的更新版本,並在控制檯上打印一條新消息。
ClassLoader在findClass方法中搜索類文件的字節碼。找到字節碼並將其讀入程序後,能夠在調用defineClass以前修改它們。例如,在調用defineClass以前,可能會將額外的調試信息添加到類文件中。某些安全應用程序的類文件數據能夠加密存儲在存儲庫中;findClass方法能夠在調用defineClass以前解密數據。 程序能夠動態生成字節碼,而不是從存儲庫中檢索它們。 這構成了JSP技術的基礎。
因爲ClassLoader負責將代碼引入JVM,所以它的架構使得平臺的安全性不會受到影響。 每一個ClassLoader爲它加載的類定義一個單獨的命名空間,所以在運行時,一個類由其包名和加載它的ClassLoader惟一標識。一個類在其命名空間以外是不可見的;在運行時,在單獨的命名空間中存在的類之間存在保護屏障。父委託模型使ClassLoader能夠請求由其父級加載的類,所以ClassLoader不須要加載它所需的全部類。
Java運行時存在的各類類加載器具備不一樣能夠從中加載代碼的存儲庫。分離存儲庫位置能夠將不一樣的信任級別分配給不一樣的存儲庫。由引導程序ClassLoader加載的Java運行時庫在JVM中具備最高級別的信任。用戶定義的ClassLoader的存儲庫具備較低的信任級別。此外,ClassLoaders能夠將每一個加載的類分配到保護域中。要根據系統安全策略(java.security.Policy的一個實例)定義代碼權限,自定義ClassLoader應擴展java.security.SecureClassLoader類並調用其defineClass方法,該方法將java.security.CodeSource對象做爲參數。SecureClassLoader的defineClass方法從系統策略中獲取與CodeSource關聯的權限,並基於此定義java.security.Protection域。有關安全模型的詳細討論超出了本文的範圍。更多細節能夠從Bill Venners的Inside the Java Virtual Machine一書中得到。
ClassLoaders提供了一種強大的機制,經過它能夠在運行時以有趣的方式擴展Java平臺。 自定義類加載器可用於實現正常運行的Java程序可用的功能。 本文已討論了其中一些應用程序。ClassLoaders在當前J2EE平臺提供的一些技術中發揮着重要做用。有關Java類加載機制的更多詳細信息,請閱讀Java虛擬機內部。