今天小編給大家分享一下JAVA的classloader怎么編寫的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識(shí),所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來(lái)了解一下吧。
創(chuàng)新互聯(lián)公司憑借專業(yè)的設(shè)計(jì)團(tuán)隊(duì)扎實(shí)的技術(shù)支持、優(yōu)質(zhì)高效的服務(wù)意識(shí)和豐厚的資源優(yōu)勢(shì),提供專業(yè)的網(wǎng)站策劃、做網(wǎng)站、網(wǎng)站設(shè)計(jì)、網(wǎng)站優(yōu)化、軟件開發(fā)、網(wǎng)站改版等服務(wù),在成都十余年的網(wǎng)站建設(shè)設(shè)計(jì)經(jīng)驗(yàn),為成都數(shù)千家中小型企業(yè)策劃設(shè)計(jì)了網(wǎng)站。
在流行的商業(yè)化編程語(yǔ)言中,Java 語(yǔ)言由于在 Java 虛擬機(jī) (JVM) 上運(yùn)行而顯得與眾不同。這意味著已編譯的程序是一種特殊的、獨(dú)立于平臺(tái)的格式,并非依賴于它們所運(yùn)行的機(jī)器。在很大程度上,這種格式不同于傳統(tǒng)的可執(zhí)行程序格式。
與 C 或 C++ 編寫的程序不同,Java 程序并不是一個(gè)可執(zhí)行文件,而是由許多獨(dú)立的類文件組成,每一個(gè)文件對(duì)應(yīng)于一個(gè) Java 類。
此外,這些類文件并非立即全部都裝入內(nèi)存,而是根據(jù)程序需要裝入內(nèi)存。ClassLoader 是 JVM 中將類裝入內(nèi)存的那部分。
而且,Java ClassLoader 就是用 Java 語(yǔ)言編寫的。這意味著創(chuàng)建您自己的 ClassLoader 非常容易,不必了解 JVM 的微小細(xì)節(jié)。
如果 JVM 已經(jīng)有一個(gè) ClassLoader,那么為什么還要編寫另一個(gè)呢?問(wèn)得好。缺省的 ClassLoader 只知道如何從本地文件系統(tǒng)裝入類文件。不過(guò)這只適合于常規(guī)情況,即已全部編譯完 Java 程序,并且計(jì)算機(jī)處于等待狀態(tài)。
但 Java 語(yǔ)言最具新意的事就是 JVM 可以非常容易地從那些非本地硬盤或從網(wǎng)絡(luò)上獲取類。例如,瀏覽者可以使用定制的 ClassLoader 從 web 站點(diǎn)裝入可執(zhí)行內(nèi)容。
有許多其它方式可以獲取類文件。除了簡(jiǎn)單地從本地或網(wǎng)絡(luò)裝入文件以外,可以使用定制的 ClassLoader 完成以下任務(wù):
在執(zhí)行非置信代碼之前,自動(dòng)驗(yàn)證數(shù)字簽名
使用用戶提供的密碼透明地解密代碼
動(dòng)態(tài)地創(chuàng)建符合用戶特定需要的定制化構(gòu)建類
任何您認(rèn)為可以生成 Java 字節(jié)碼的內(nèi)容都可以集成到應(yīng)用程序中。
如果使用過(guò) jdk 或任何基于 Java 瀏覽器中的 Applet 查看器,那么您差不多肯定使用過(guò)定制的 ClassLoader。
Sun 最初發(fā)布 Java 語(yǔ)言時(shí),其中最令人興奮的一件事是觀看這項(xiàng)新技術(shù)是如何執(zhí)行在運(yùn)行時(shí)從遠(yuǎn)程的 Web 服務(wù)器裝入的代碼。(此外,還有更令人興奮的事 -- Java 技術(shù)提供了一種便于編寫代碼的強(qiáng)大語(yǔ)言。)更一些令人激動(dòng)的是它可以執(zhí)行從遠(yuǎn)程 Web 服務(wù)器通過(guò) HTTP 連接發(fā)送過(guò)來(lái)的字節(jié)碼。
此項(xiàng)功能歸功于 Java 語(yǔ)言可以安裝定制 ClassLoader。Applet 查看器包含一個(gè) ClassLoader,它不在本地文件系統(tǒng)中尋找類,而是訪問(wèn)遠(yuǎn)程服務(wù)器上的 Web 站點(diǎn),經(jīng)過(guò) HTTP 裝入原始的字節(jié)碼文件,并把它們轉(zhuǎn)換成 JVM 內(nèi)的類。
瀏覽器和 Applet 查看器中的 ClassLoaders 還可以做其它事情:它們支持安全性以及使不同的 Applet 在不同的頁(yè)面上運(yùn)行而互不干擾。
Luke Gorrie 編寫的 Echidna 是一個(gè)開放源碼包,它可以使您在單個(gè)虛擬機(jī)上運(yùn)行多個(gè) Java 應(yīng)用程序。它使用定制的 ClassLoader,通過(guò)向每個(gè)應(yīng)用程序提供該類文件的自身副本,以防止應(yīng)用程序互相干擾。
了解了 ClassLoader 如何工作以及如何編寫 ClassLoader 之后,我們將創(chuàng)建稱作 CompilingClassLoader (CCL) 的 Classloader。CCL 為我們編譯 Java 代碼,而無(wú)需要我們干涉這個(gè)過(guò)程。它基本上就類似于直接構(gòu)建到運(yùn)行時(shí)系統(tǒng)中的 "make" 程序。
注:進(jìn)一步了解之前,應(yīng)注意在 JDK 版本 1.2 中已改進(jìn)了 ClassLoader 系統(tǒng)的某些方面(即 Java 2 平臺(tái))。本教程是按 JDK 版本 1.0 和 1.1 寫的,但也可以在以后的版本中運(yùn)行。
Java 2 中 ClassLoader 的變動(dòng)描述了 Java 版本 1.2 中的變動(dòng),并提供了一些詳細(xì)信息,以便修改 ClassLoader 來(lái)利用這些變動(dòng)。
ClassLoader 的基本目標(biāo)是對(duì)類的請(qǐng)求提供服務(wù)。當(dāng) JVM 需要使用類時(shí),它根據(jù)名稱向 ClassLoader 請(qǐng)求這個(gè)類,然后 ClassLoader 試圖返回一個(gè)表示這個(gè)類的 Class 對(duì)象。 通過(guò)覆蓋對(duì)應(yīng)于這個(gè)過(guò)程不同階段的方法,可以創(chuàng)建定制的 ClassLoader。
在本文的其余部分,您會(huì)學(xué)習(xí) Java ClassLoader 的關(guān)鍵方法。您將了解每一個(gè)方法的作用以及它是如何適合裝入類文件這個(gè)過(guò)程的。您也會(huì)知道,創(chuàng)建自己的 ClassLoader 時(shí),需要編寫什么代碼。
在下文中,您將會(huì)利用這些知識(shí)來(lái)使用我們的 ClassLoader 示例 -- CompilingClassLoader。
ClassLoader.loadClass() 是 ClassLoader 的入口點(diǎn)。其特征如下:
Class loadClass( String name, boolean resolve );
name 參數(shù)指定了 JVM 需要的類的名稱,該名稱以包表示法表示,如 Foo 或 java.lang.object。 resolve 參數(shù)告訴方法是否需要解析類。在準(zhǔn)備執(zhí)行類之前,應(yīng)考慮類解析。并不總是需要解析。如果 JVM 只需要知道該類是否存在或找出該類的超類,那么就不需要解析。
在 Java 版本 1.1 和以前的版本中,loadClass 方法是創(chuàng)建定制的 ClassLoader 時(shí)唯一需要覆蓋的方法。(Java 2 中 ClassLoader 的變動(dòng)提供了關(guān)于 Java 1.2 中 findClass() 方法的信息。)
defineClass 方法是 ClassLoader 的主要訣竅。該方法接受由原始字節(jié)組成的數(shù)組并把它轉(zhuǎn)換成 Class 對(duì)象。原始數(shù)組包含如從文件系統(tǒng)或網(wǎng)絡(luò)裝入的數(shù)據(jù)。
defineClass 管理 JVM 的許多復(fù)雜、神秘和倚賴于實(shí)現(xiàn)的方面 -- 它把字節(jié)碼分析成運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)、校驗(yàn)有效性等等。不必?fù)?dān)心,您無(wú)需親自編寫它。事實(shí)上,即使您想要這么做也不能覆蓋它,因?yàn)樗驯粯?biāo)記成最終的。
findSystemClass 方法從本地文件系統(tǒng)裝入文件。它在本地文件系統(tǒng)中尋找類文件,如果存在,就使用 defineClass 將原始字節(jié)轉(zhuǎn)換成 Class 對(duì)象,以將該文件轉(zhuǎn)換成類。當(dāng)運(yùn)行 Java 應(yīng)用程序時(shí),這是 JVM 正常裝入類的缺省機(jī)制。(Java 2 中 ClassLoader 的變動(dòng)提供了關(guān)于 Java 版本 1.2 這個(gè)過(guò)程變動(dòng)的詳細(xì)信息。)
對(duì)于定制的 ClassLoader,只有在嘗試其它方法裝入類之后,再使用 findSystemClass。原因很簡(jiǎn)單:ClassLoader 是負(fù)責(zé)執(zhí)行裝入類的特殊步驟,不是負(fù)責(zé)所有類。例如,即使 ClassLoader 從遠(yuǎn)程的 Web 站點(diǎn)裝入了某些類,仍然需要在本地機(jī)器上裝入大量的基本 Java 庫(kù)。而這些類不是我們所關(guān)心的,所以要 JVM 以缺省方式裝入它們:從本地文件系統(tǒng)。這就是 findSystemClass 的用途。
其工作流程如下:
請(qǐng)求定制的 ClassLoader 裝入類。
檢查遠(yuǎn)程 Web 站點(diǎn),查看是否有所需要的類。
如果有,那么好;抓取這個(gè)類,完成任務(wù)。
如果沒(méi)有,假定這個(gè)類是在基本 Java 庫(kù)中,那么調(diào)用 findSystemClass,使它從文件系統(tǒng)裝入該類。
在大多數(shù)定制 ClassLoaders 中,首先調(diào)用 findSystemClass 以節(jié)省在本地就可以裝入的許多 Java 庫(kù)類而要在遠(yuǎn)程 Web 站點(diǎn)上查找所花的時(shí)間。然而,正如,在下一章節(jié)所看到的,直到確信能自動(dòng)編譯我們的應(yīng)用程序代碼時(shí),才讓 JVM 從本地文件系統(tǒng)裝入類。
正如前面所提到的,可以不完全地(不帶解析)裝入類,也可以完全地(帶解析)裝入類。當(dāng)編寫我們自己的 loadClass 時(shí),可以調(diào)用 resolveClass,這取決于 loadClass 的 resolve 參數(shù)的值。
findLoadedClass 充當(dāng)一個(gè)緩存:當(dāng)請(qǐng)求 loadClass 裝入類時(shí),它調(diào)用該方法來(lái)查看 ClassLoader 是否已裝入這個(gè)類,這樣可以避免重新裝入已存在類所造成的麻煩。應(yīng)首先調(diào)用該方法。
讓我們看一下如何組裝所有方法。
我們的 loadClass 實(shí)現(xiàn)示例執(zhí)行以下步驟。(這里,我們沒(méi)有指定生成類文件是采用了哪種技術(shù) -- 它可以是從.NET 上裝入、或者從歸檔文件中提取、或者實(shí)時(shí)編譯。無(wú)論是哪一種,那是種特殊的神奇方式,使我們獲得了原始類文件字節(jié)。)
我們的 ClassLoader (CCL) 的任務(wù)是確保代碼被編譯和更新。
下面描述了它的工作方式:
當(dāng)請(qǐng)求一個(gè)類時(shí),先查看它是否在磁盤的當(dāng)前目錄或相應(yīng)的子目錄。
如果該類不存在,但源碼中有,那么調(diào)用 Java 編譯器來(lái)生成類文件。
如果該類已存在,檢查它是否比源碼舊。如果是,調(diào)用 Java 編譯器來(lái)重新生成類文件。
如果編譯失敗,或者由于其它原因不能從現(xiàn)有的源碼中生成類文件,返回 ClassNotFoundException。
如果仍然沒(méi)有該類,也許它在其它庫(kù)中,所以調(diào)用 findSystemClass 來(lái)尋找該類。
如果還是沒(méi)有,則返回 ClassNotFoundException。
否則,返回該類。
調(diào)用 findLoadedClass 來(lái)查看是否存在已裝入的類。
如果沒(méi)有,那么采用那種特殊的神奇方式來(lái)獲取原始字節(jié)。
如果已有原始字節(jié),調(diào)用 defineClass 將它們轉(zhuǎn)換成 Class 對(duì)象。
如果沒(méi)有原始字節(jié),然后調(diào)用 findSystemClass 查看是否從本地文件系統(tǒng)獲取類。
如果 resolve 參數(shù)是 true,那么調(diào)用 resolveClass 解析 Class 對(duì)象。
如果還沒(méi)有類,返回 ClassNotFoundException。
否則,將類返回給調(diào)用程序。
在深入討論之前,應(yīng)該先退一步,討論 Java 編譯。通常,Java 編譯器不只是編譯您要求它編譯的類。它還會(huì)編譯其它類,如果這些類是您要求編譯的類所需要的類。
CCL 逐個(gè)編譯應(yīng)用程序中的需要編譯的每一個(gè)類。但一般來(lái)說(shuō),在編譯器編譯完第一個(gè)類后,CCL 會(huì)查找所有需要編譯的類,然后編譯它。為什么?Java 編譯器類似于我們正在使用的規(guī)則:如果類不存在,或者與它的源碼相比,它比較舊,那么它需要編譯。其實(shí),Java 編譯器在 CCL 之前的一個(gè)步驟,它會(huì)做大部分的工作。
當(dāng) CCL 編譯它們時(shí),會(huì)報(bào)告它正在編譯哪個(gè)應(yīng)用程序上的類。在大多數(shù)的情況下,CCL 會(huì)在程序中的主類上調(diào)用編譯器,它會(huì)做完所有要做的 -- 編譯器的單一調(diào)用已足夠了。
然而,有一種情形,在第一步時(shí)不會(huì)編譯某些類。如果使用 Class.forName 方法,通過(guò)名稱來(lái)裝入類,Java 編譯器會(huì)不知道這個(gè)類時(shí)所需要的。在這種情況下,您會(huì)看到 CCL 再次運(yùn)行 Java 編譯器來(lái)編譯這個(gè)類。在源代碼中演示了這個(gè)過(guò)程。
要使用 CCL,必須以特殊方式調(diào)用程序。不能直接運(yùn)行該程序,如: % java Foo arg1 arg2
應(yīng)以下列方式運(yùn)行它:
% java CCLRun Foo arg1 arg2
CCLRun 是一個(gè)特殊的存根程序,它創(chuàng)建 CompilingClassLoader 并用它來(lái)裝入程序的主類,以確保通過(guò) CompilingClassLoader 來(lái)裝入整個(gè)程序。CCLRun 使用 Java Reflection api 來(lái)調(diào)用特定類的主方法并把參數(shù)傳遞給它。有關(guān)詳細(xì)信息,請(qǐng)參閱源代碼。
源碼包括了一組小類,它們演示了工作方式。主程序是 Foo 類,它創(chuàng)建類 Bar 的實(shí)例。類 Bar 創(chuàng)建另一個(gè)類 Baz 的實(shí)例,它在 baz 包內(nèi),這是為了展示 CCL 是如何處理子包里的代碼。Bar 也是通過(guò)名稱裝入的,其名稱為 Boo,這用來(lái)展示它也能與 CCL 工作。
每個(gè)類都聲明已被裝入并運(yùn)行?,F(xiàn)在用源代碼來(lái)試一下。編譯 CCLRun 和 CompilingClassLoader。確保不要編譯其它類(Foo、Bar、Baz 和 Boo),否則將不會(huì)使用 CCL,因?yàn)檫@些類已經(jīng)編譯過(guò)了。
% java CCLRun Foo arg1 arg2
CCL: Compiling Foo.java...
foo! arg1 arg2
bar! arg1 arg2
baz! arg1 arg2
CCL: Compiling Boo.java...
Boo!
請(qǐng)注意,首先調(diào)用編譯器,F(xiàn)oo.java 管理 Bar 和 baz.Baz。直到 Bar 通過(guò)名稱來(lái)裝入 Boo 時(shí),被調(diào)用它,這時(shí) CCL 會(huì)再次調(diào)用編譯器來(lái)編譯它。
Foo.java
以下是 Foo.java 的源代碼
// $Id$ public class Foo { static public void main( String args[] ) throws Exception { System.out.println( "foo! "+args[0]+" "+args[1] ); new Bar( args[0], args[1] ); } }
Bar.java
以下是 Bar.java 的源代碼
// $Id$ import baz.*; public class Bar { public Bar( String a, String b ) { System.out.println( "bar! "+a+" "+b ); new Baz( a, b ); try { Class booClass = Class.forName( "Boo" ); Object boo = booClass.newInstance(); } catch( Exception e ) { e.printStackTrace(); } } }
baz/Baz.java
以下是 baz/Baz.java 的源代碼
// $Id$ package baz; public class Baz { public Baz( String a, String b ) { System.out.println( "baz! "+a+" "+b ); } }
Boo.java
以下是 Boo.java 的源代碼
// $Id$ public class Boo { public Boo() { System.out.println( "Boo!" ); } }
以上就是“JAVA的classloader怎么編寫”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會(huì)為大家更新不同的知識(shí),如果還想學(xué)習(xí)更多的知識(shí),請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。