小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

了解Java ClassLoader

 730 2008-07-03
【原文地址:https://www6.software.ibm.com/developerworks/cn/education/java/j-classloader/tutorial/】
1.介紹
2.ClassLoader的結(jié)構(gòu)
3.Compiling ClassLoader
4.java2 中ClassLoader的變動
5.源代碼
---------------------------------------------------------------------------

第一章 介紹

什么是 ClassLoader

在流行的商業(yè)化編程語言中,Java 語言由于在 Java 虛擬機(jī) (JVM) 上運(yùn)行而顯得與眾不同。這意味著已編譯的程序是一種特殊的、獨(dú)立于平臺的格式,并非依賴于它們所運(yùn)行的機(jī)器。在很大程度上,這種格式不同于傳統(tǒng)的可執(zhí)行程序格式。

與 C 或 C++ 編寫的程序不同,Java 程序并不是一個可執(zhí)行文件,而是由許多獨(dú)立的類文件組成,每一個文件對應(yīng)于一個 Java 類。

此外,這些類文件并非立即全部都裝入內(nèi)存,而是根據(jù)程序需要裝入內(nèi)存。ClassLoader 是 JVM 中將類裝入內(nèi)存的那部分。

而且,Java ClassLoader 就是用 Java 語言編寫的。這意味著創(chuàng)建您自己的 ClassLoader 非常容易,不必了解 JVM 的微小細(xì)節(jié)。

為什么編寫 ClassLoader?

如果 JVM 已經(jīng)有一個 ClassLoader,那么為什么還要編寫另一個呢?問得好。缺省的 ClassLoader 只知道如何從本地文件系統(tǒng)裝入類文件。不過這只適合于常規(guī)情況,即已全部編譯完 Java 程序,并且計算機(jī)處于等待狀態(tài)。

但 Java 語言最具新意的事就是 JVM 可以非常容易地從那些非本地硬盤或從網(wǎng)絡(luò)上獲取類。例如,瀏覽者可以使用定制的 ClassLoader 從 Web 站點(diǎn)裝入可執(zhí)行內(nèi)容。

有許多其它方式可以獲取類文件。除了簡單地從本地或網(wǎng)絡(luò)裝入文件以外,可以使用定制的 ClassLoader 完成以下任務(wù):

  • 在執(zhí)行非置信代碼之前,自動驗(yàn)證數(shù)字簽名
  • 使用用戶提供的密碼透明地解密代碼
  • 動態(tài)地創(chuàng)建符合用戶特定需要的定制化構(gòu)建類
任何您認(rèn)為可以生成 Java 字節(jié)碼的內(nèi)容都可以集成到應(yīng)用程序中。

定制 ClassLoader 示例

如果使用過 JDK 或任何基于 Java 瀏覽器中的 Applet 查看器,那么您差不多肯定使用過定制的 ClassLoader。

Sun 最初發(fā)布 Java 語言時,其中最令人興奮的一件事是觀看這項(xiàng)新技術(shù)是如何執(zhí)行在運(yùn)行時從遠(yuǎn)程的 Web 服務(wù)器裝入的代碼。(此外,還有更令人興奮的事 -- Java 技術(shù)提供了一種便于編寫代碼的強(qiáng)大語言。)更一些令人激動的是它可以執(zhí)行從遠(yuǎn)程 Web 服務(wù)器通過 HTTP 連接發(fā)送過來的字節(jié)碼。

此 項(xiàng)功能歸功于 Java 語言可以安裝定制 ClassLoader。Applet 查看器包含一個 ClassLoader,它不在本地文件系統(tǒng)中尋找類,而是訪問遠(yuǎn)程服務(wù)器上的 Web 站點(diǎn),經(jīng)過 HTTP 裝入原始的字節(jié)碼文件,并把它們轉(zhuǎn)換成 JVM 內(nèi)的類。

瀏覽器和 Applet 查看器中的 ClassLoaders 還可以做其它事情:它們支持安全性以及使不同的 Applet 在不同的頁面上運(yùn)行而互不干擾。

Luke Gorrie 編寫的 Echidna 是一個開放源碼包,它可以使您在單個虛擬機(jī)上運(yùn)行多個 Java 應(yīng)用程序。(請參閱進(jìn)一步了解和參考資料。)它使用定制的 ClassLoader,通過向每個應(yīng)用程序提供該類文件的自身副本,以防止應(yīng)用程序互相干擾。


我們的 ClassLoader 示例

了解了 ClassLoader 如何工作以及如何編寫 ClassLoader 之后,我們將創(chuàng)建稱作 CompilingClassLoader (CCL) 的 Classloader。CCL 為我們編譯 Java 代碼,而無需要我們干涉這個過程。它基本上就類似于直接構(gòu)建到運(yùn)行時系統(tǒng)中的 "make" 程序。

注:進(jìn)一步了解之前,應(yīng)注意在 JDK 版本 1.2 中已改進(jìn)了 ClassLoader 系統(tǒng)的某些方面(即 Java 2 平臺)。本教程是按 JDK 版本 1.0 和 1.1 寫的,但也可以在以后的版本中運(yùn)行。

Java 2 中 ClassLoader 的變動描述了 Java 版本 1.2 中的變動,并提供了一些詳細(xì)信息,以便修改 ClassLoader 來利用這些變動。

 


 

------------------------------------------------------------------------------------------------------

第二章.ClassLoader的結(jié)構(gòu)



ClassLoader 的基本目標(biāo)是對類的請求提供服務(wù)。當(dāng) JVM 需要使用類時,它根據(jù)名稱向 ClassLoader 請求這個類,然后 ClassLoader 試圖返回一個表示這個類的 Class 對象。

通過覆蓋對應(yīng)于這個過程不同階段的方法,可以創(chuàng)建定制的 ClassLoader。

在本章的其余部分,您會學(xué)習(xí) Java ClassLoader 的關(guān)鍵方法。您將了解每一個方法的作用以及它是如何適合裝入類文件這個過程的。您也會知道,創(chuàng)建自己的 ClassLoader 時,需要編寫什么代碼。

在下一章中,您將會利用這些知識來使用我們的 ClassLoader 示例 -- CompilingClassLoader。


方法 loadClass


ClassLoader.loadClass() 是 ClassLoader 的入口點(diǎn)。其特征如下:

 

Class loadClass( String name, boolean resolve );

name 參數(shù)指定了 JVM 需要的類的名稱,該名稱以包表示法表示,如 Foojava.lang.Object。

resolve 參數(shù)告訴方法是否需要解析類。在準(zhǔn)備執(zhí)行類之前,應(yīng)考慮類解析。并不總是需要解析。如果 JVM 只需要知道該類是否存在或找出該類的超類,那么就不需要解析。

在 Java 版本 1.1 和以前的版本中,loadClass 方法是創(chuàng)建定制的 ClassLoader 時唯一需要覆蓋的方法。(Java 2 中 ClassLoader 的變動提供了關(guān)于 Java 1.2 中 findClass() 方法的信息。)


方法 defineClass

defineClass 方法是 ClassLoader 的主要訣竅。該方法接受由原始字節(jié)組成的數(shù)組并把它轉(zhuǎn)換成 Class 對象。原始數(shù)組包含如從文件系統(tǒng)或網(wǎng)絡(luò)裝入的數(shù)據(jù)。

defineClass 管理 JVM 的許多復(fù)雜、神秘和倚賴于實(shí)現(xiàn)的方面 -- 它把字節(jié)碼分析成運(yùn)行時數(shù)據(jù)結(jié)構(gòu)、校驗(yàn)有效性等等。不必?fù)?dān)心,您無需親自編寫它。事實(shí)上,即使您想要這么做也不能覆蓋它,因?yàn)樗驯粯?biāo)記成最終的。


方法 findSystemClass

findSystemClass 方法從本地文件系統(tǒng)裝入文件。它在本地文件系統(tǒng)中尋找類文件,如果存在,就使用 defineClass 將原始字節(jié)轉(zhuǎn)換成 Class 對象,以將該文件轉(zhuǎn)換成類。當(dāng)運(yùn)行 Java 應(yīng)用程序時,這是 JVM 正常裝入類的缺省機(jī)制。(Java 2 中 ClassLoader 的變動提供了關(guān)于 Java 版本 1.2 這個過程變動的詳細(xì)信息。)

對于定制的 ClassLoader,只有在嘗試其它方法裝入類之后,再使用 findSystemClass。原因很簡單:ClassLoader 是負(fù)責(zé)執(zhí)行裝入類的特殊步驟,不是負(fù)責(zé)所有類。例如,即使 ClassLoader 從遠(yuǎn)程的 Web 站點(diǎn)裝入了某些類,仍然需要在本地機(jī)器上裝入大量的基本 Java 庫。而這些類不是我們所關(guān)心的,所以要 JVM 以缺省方式裝入它們:從本地文件系統(tǒng)。這就是 findSystemClass 的用途。

其工作流程如下:

 

  • 請求定制的 ClassLoader 裝入類。
  • 檢查遠(yuǎn)程 Web 站點(diǎn),查看是否有所需要的類。
  • 如果有,那么好;抓取這個類,完成任務(wù)。
  • 如果沒有,假定這個類是在基本 Java 庫中,那么調(diào)用 findSystemClass,使它從文件系統(tǒng)裝入該類。

在大多數(shù)定制 ClassLoaders 中,首先調(diào)用 findSystemClass 以節(jié)省在本地就可以裝入的許多 Java 庫類而要在遠(yuǎn)程 Web 站點(diǎn)上查找所花的時間。然而,正如,在下一章節(jié)所看到的,直到確信能自動編譯我們的應(yīng)用程序代碼時,才讓 JVM 從本地文件系統(tǒng)裝入類。


方法 resolveClass

正如前面所提到的,可以不完全地(不帶解析)裝入類,也可以完全地(帶解析)裝入類。當(dāng)編寫我們自己的 loadClass 時,可以調(diào)用 resolveClass,這取決于 loadClassresolve 參數(shù)的值。

方法 findLoadedClass

findLoadedClass 充當(dāng)一個緩存:當(dāng)請求 loadClass 裝入類時,它調(diào)用該方法來查看 ClassLoader 是否已裝入這個類,這樣可以避免重新裝入已存在類所造成的麻煩。應(yīng)首先調(diào)用該方法。

組裝

 

 

 

讓我們看一下如何組裝所有方法。

我們的 loadClass 實(shí)現(xiàn)示例執(zhí)行以下步驟。(這里,我們沒有指定生成類文件是采用了哪種技術(shù) -- 它可以是從 Net 上裝入、或者從歸檔文件中提取、或者實(shí)時編譯。無論是哪一種,那是種特殊的神奇方式,使我們獲得了原始類文件字節(jié)。)

 

  • 調(diào)用 findLoadedClass 來查看是否存在已裝入的類。

  • 如果沒有,那么采用那種特殊的神奇方式來獲取原始字節(jié)。

  • 如果已有原始字節(jié),調(diào)用 defineClass 將它們轉(zhuǎn)換成 Class 對象。

  • 如果沒有原始字節(jié),然后調(diào)用 findSystemClass 查看是否從本地文件系統(tǒng)獲取類。

  • 如果 resolve 參數(shù)是 true,那么調(diào)用 resolveClass 解析 Class 對象。

  • 如果還沒有類,返回 ClassNotFoundException。

  • 否則,將類返回給調(diào)用程序。
推想

現(xiàn)在您已經(jīng)了解了 ClassLoader 的工作原理,現(xiàn)在該構(gòu)建一個了。在下一章中,我們將討論 CCL。

---------------------------------------------------------------------------------------------

第三章:Compiling ClassLoader

CCL 揭密

我們的 ClassLoader (CCL) 的任務(wù)是確保代碼被編譯和更新。

下面描述了它的工作方式:

 

  • 當(dāng)請求一個類時,先查看它是否在磁盤的當(dāng)前目錄或相應(yīng)的子目錄。

  • 如果該類不存在,但源碼中有,那么調(diào)用 Java 編譯器來生成類文件。

  • 如果該類已存在,檢查它是否比源碼舊。如果是,調(diào)用 Java 編譯器來重新生成類文件。

  • 如果編譯失敗,或者由于其它原因不能從現(xiàn)有的源碼中生成類文件,返回 ClassNotFoundException。

  • 如果仍然沒有該類,也許它在其它庫中,所以調(diào)用 findSystemClass 來尋找該類。

  • 如果還是沒有,則返回 ClassNotFoundException

  • 否則,返回該類。
Java 編譯的工作方式

 

在深入討論之前,應(yīng)該先退一步,討論 Java 編譯。通常,Java 編譯器不只是編譯您要求它編譯的類。它還會編譯其它類,如果這些類是您要求編譯的類所需要的類。

CCL 逐個編譯應(yīng)用程序中的需要編譯的每一個類。但一般來說,在編譯器編譯完第一個類后,CCL 會查找所有需要編譯的類,然后編譯它。為什么?Java 編譯器類似于我們正在使用的規(guī)則:如果類不存在,或者與它的源碼相比,它比較舊,那么它需要編譯。其實(shí),Java 編譯器在 CCL 之前的一個步驟,它會做大部分的工作。

當(dāng) CCL 編譯它們時,會報告它正在編譯哪個應(yīng)用程序上的類。在大多數(shù)的情況下,CCL 會在程序中的主類上調(diào)用編譯器,它會做完所有要做的 -- 編譯器的單一調(diào)用已足夠了。

然而,有一種情形,在第一步時不會編譯某些類。如果使用 Class.forName 方法,通過名稱來裝入類,Java 編譯器會不知道這個類時所需要的。在這種情況下,您會看到 CCL 再次運(yùn)行 Java 編譯器來編譯這個類。在源代碼中演示了這個過程。

使用 CompilationClassLoader

要使用 CCL,必須以特殊方式調(diào)用程序。不能直接運(yùn)行該程序,如:

 

% java Foo arg1 arg2

應(yīng)以下列方式運(yùn)行它:

 

% java CCLRun Foo arg1 arg2

CCLRun 是一個特殊的存根程序,它創(chuàng)建 CompilingClassLoader 并用它來裝入程序的主類,以確保通過 CompilingClassLoader 來裝入整個程序。CCLRun 使用 Java Reflection API 來調(diào)用特定類的主方法并把參數(shù)傳遞給它。有關(guān)詳細(xì)信息,請參閱源代碼

運(yùn)行示例

源碼包括了一組小類,它們演示了工作方式。主程序是 Foo 類,它創(chuàng)建類 Bar 的實(shí)例。類 Bar 創(chuàng)建另一個類 Baz 的實(shí)例,它在 baz 包內(nèi),這是為了展示 CCL 是如何處理子包里的代碼。Bar 也是通過名稱裝入的,其名稱為 Boo,這用來展示它也能與 CCL 工作。

每個類都聲明已被裝入并運(yùn)行?,F(xiàn)在用源代碼來試一下。編譯 CCLRun 和 CompilingClassLoader。確保不要編譯其它類(Foo、Bar、BazBoo),否則將不會使用 CCL,因?yàn)檫@些類已經(jīng)編譯過了。

 



% java CCLRun Foo arg1 arg2

CCL: Compiling Foo.java...

foo! arg1 arg2

bar! arg1 arg2

baz! arg1 arg2

CCL: Compiling Boo.java...

Boo!

請注意,首先調(diào)用編譯器,Foo.java 管理 Barbaz.Baz。直到 Bar 通過名稱來裝入 Boo 時,被調(diào)用它,這時 CCL 會再次調(diào)用編譯器來編譯它。

 

 

 

 

--------------------------------------------------------------------------------------

第四章:java2 中ClassLoader的變動


概述

在 Java 版本 1.2 和以后的版本中,對 ClassLoader 做了一些改進(jìn)。任何為老系統(tǒng)編寫的代碼可以在新版本中運(yùn)行,但新系統(tǒng)為您提供了一些便利。

新模型是委托模型,這意味著如果 ClassLoader 不能找到類,它會請求父代 ClassLoader 來執(zhí)行此項(xiàng)任務(wù)。所有 ClassLoaders 的根是系統(tǒng) ClassLoader,它會以缺省方式裝入類 -- 即,從本地文件系統(tǒng)。

loadClass 的缺省實(shí)現(xiàn)

定制編寫的 loadClass 方法一般嘗試幾種方式來裝入所請求的類,如果您編寫許多類,會發(fā)現(xiàn)一次次地在相同的、很復(fù)雜的方法上編寫變量。

在 Java 1.2 中 loadClass 的實(shí)現(xiàn)嵌入了大多數(shù)查找類的一般方法,并使您通過覆蓋 findClass 方法來定制它,在適當(dāng)?shù)臅r候 findClass 會調(diào)用 loadClass。

這種方式的好處是您可能不一定要覆蓋 loadClass;只要覆蓋 findClass 就行了,這減少了工作量。

新方法:findClass

loadClass 的缺省實(shí)現(xiàn)調(diào)用這個新方法。findClass 的用途包含您的 ClassLoader 的所有特殊代碼,而無需要復(fù)制其它代碼(例如,當(dāng)專門的方法失敗時,調(diào)用系統(tǒng) ClassLoader)。

新方法:getSystemClassLoader

如果覆蓋 findClassloadClass,getSystemClassLoader 使您能以實(shí)際 ClassLoader 對象來訪問系統(tǒng) ClassLoader(而不是固定的從 findSystemClass 調(diào)用它)。

新方法:getParent

為了將類請求委托給父代 ClassLoader,這個新方法允許 ClassLoader 獲取它的父代 ClassLoader。當(dāng)使用特殊方法,定制的 ClassLoader 不能找到類時,可以使用這種方法。

父代 ClassLoader 被定義成創(chuàng)建該 ClassLoader 所包含代碼的對象的 ClassLoader。

----------------------------------------------------------------------------------

 

 

 

第五章.源代碼

 

CompilingClassLoader.java

以下是 CompilingClassLoader.java 的源代碼

// $Id$

import java.io.*;

/*

A CompilingClassLoader compiles your Java source on-the-fly. It checks

for nonexistent .class files, or .class files that are older than their

corresponding source code.*/

public class CompilingClassLoader extends ClassLoader

{

// Given a filename, read the entirety of that file from disk

// and return it as a byte array.

private byte[] getBytes( String filename ) throws IOException {

// Find out the length of the file

File file = new File( filename );

long len = file.length();

// Create an array that‘s just the right size for the file‘s

// contents

byte raw[] = new byte[(int)len];

// Open the file

FileInputStream fin = new FileInputStream( file );

// Read all of it into the array; if we don‘t get all,

// then it‘s an error.

int r = fin.read( raw );

if (r != len)

throw new IOException( "Can‘t read all, "+r+" != "+len );

// Don‘t forget to close the file!

fin.close();

// And finally return the file contents as an array

return raw;

}

// Spawn a process to compile the java source code file

// specified in the ‘javaFile‘ parameter. Return a true if

// the compilation worked, false otherwise.

private boolean compile( String javaFile ) throws IOException {

// Let the user know what‘s going on

System.out.println( "CCL: Compiling "+javaFile+"..." );

// Start up the compiler

Process p = Runtime.getRuntime().exec( "javac "+javaFile );

// Wait for it to finish running

try {

p.waitFor();

} catch( InterruptedException ie ) { System.out.println( ie ); }

// Check the return code, in case of a compilation error

int ret = p.exitValue();

// Tell whether the compilation worked

return ret==0;

}

// The heart of the ClassLoader -- automatically compile

// source as necessary when looking for class files

public Class loadClass( String name, boolean resolve )

throws ClassNotFoundException {



// Our goal is to get a Class object

Class clas = null;



// First, see if we‘ve already dealt with this one

clas = findLoadedClass( name );



//System.out.println( "findLoadedClass: "+clas );



// Create a pathname from the class name

// E.g. java.lang.Object => java/lang/Object

String fileStub = name.replace( ‘.‘, ‘/‘ );



// Build objects pointing to the source code (.java) and object

// code (.class)

String javaFilename = fileStub+".java";

String classFilename = fileStub+".class";



File javaFile = new File( javaFilename );

File classFile = new File( classFilename );



//System.out.println( "j "+javaFile.lastModified()+" c "+

// classFile.lastModified() );



// First, see if we want to try compiling. We do if (a) there

// is source code, and either (b0) there is no object code,

// or (b1) there is object code, but it‘s older than the source

if (javaFile.exists() &&

(!classFile.exists() ||

javaFile.lastModified() > classFile.lastModified())) {



try {

// Try to compile it. If this doesn‘t work, then

// we must declare failure. (It‘s not good enough to use

// and already-existing, but out-of-date, classfile)

if (!compile( javaFilename ) || !classFile.exists()) {

throw new ClassNotFoundException( "Compile failed: "+javaFilename );

}

} catch( IOException ie ) {



// Another place where we might come to if we fail

// to compile

throw new ClassNotFoundException( ie.toString() );

}

}



// Let‘s try to load up the raw bytes, assuming they were

// properly compiled, or didn‘t need to be compiled

try {



// read the bytes

byte raw[] = getBytes( classFilename );



// try to turn them into a class

clas = defineClass( name, raw, 0, raw.length );

} catch( IOException ie ) {

// This is not a failure! If we reach here, it might

// mean that we are dealing with a class in a library,

// such as java.lang.Object

}



//System.out.println( "defineClass: "+clas );



// Maybe the class is in a library -- try loading

// the normal way

if (clas==null) {

clas = findSystemClass( name );

}



//System.out.println( "findSystemClass: "+clas );



// Resolve the class, if any, but only if the "resolve"

// flag is set to true

if (resolve && clas != null)

resolveClass( clas );



// If we still don‘t have a class, it‘s an error

if (clas == null)

throw new ClassNotFoundException( name );



// Otherwise, return the class

return clas;

}


 }

CCRun.java




以下是 CCRun.java 的源代碼




// $Id$



import java.lang.reflect.*;



/*



CCLRun executes a Java program by loading it through a

CompilingClassLoader.



*/



public class CCLRun

{

static public void main( String args[] ) throws Exception {



// The first argument is the Java program (class) the user

// wants to run

String progClass = args[0];



// And the arguments to that program are just

// arguments 1..n, so separate those out into

// their own array

String progArgs[] = new String[args.length-1];

System.arraycopy( args, 1, progArgs, 0, progArgs.length );



// Create a CompilingClassLoader

CompilingClassLoader ccl = new CompilingClassLoader();



// Load the main class through our CCL

Class clas = ccl.loadClass( progClass );



// Use reflection to call its main() method, and to

// pass the arguments in.



// Get a class representing the type of the main method‘s argument

Class mainArgType[] = { (new String[0]).getClass() };



// Find the standard main method in the class

Method main = clas.getMethod( "main", mainArgType );



// Create a list containing the arguments -- in this case,

// an array of strings

Object argsArray[] = { progArgs };



// Call the method

main.invoke( null, argsArray );

}

}

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!" );

}

}










    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多