實現JAVA的動態類載入機制

實現JAVA的動態類載入機制

----作爲充分利用Java的動態類載入機制的最好例子,帶有Java擴展的Web瀏覽器根據請求從網絡或本地文件系統中動態加載Javaapplet(遵循一定規則的Java小應用程序類),然後在本地系統中執行它,大大增強了主頁的功能。

----其實,Java本身就是一種極具動態性的語言。類似Windows的動態鏈接庫(DLL),Java應用程序總是被編譯成若干個單獨的class文件,程序執行時根據需要由Java虛擬機動態載入相應的類。這種機制使編寫動態的分佈式應用程序成爲可能:我們可以在客戶端編寫自己的類載入器,而真正執行的程序卻存放在本地、局域網或世界另一端的主機上。下面將介紹如何在自己的應用程序中實現Java的動態類載入機制。

與動態類載入有關的系統類
----爲支持動態類載入機制,在系統類組java.lang中提供了兩個類:Class類和ClassLoader類。

----1、類java.lang.Class。在Java虛擬機中,每一個類或接口都是由Class類來操縱的,它不能被顯式的實例化,必須用其他方法來獲取Class類的對象。動態類載入機制的關鍵一步在於如何獲得指定類的Class類型的對象。相關方法主要有:


---- public static Class forName(String className)

----這是一個靜態方法,它獲取指定名字的類的Class類型對象,類名可以是象“sun.applet.Applet”這樣的字符串,但不能帶有路徑或網絡地址等信息。這是從本地系統中動態載入類的最方便的辦法。

---- public Object newInstance()

----這是最重要的一個方法,它建立由Class類型對象描述的指定類的實例。

----下面是一個用forName()和newInstance()方法實現動態類載入的代碼,share類包含一個接口,詳細內容將在第三部分中解釋。

try{
//根據類名建立Class類型的對象。
Class cc =Class.forName("類名"));
//建立被載入類類的實例並強制類型轉換,
值賦給share類型的變量。
share oo=((share)cc).newInstance();
//調用該類的方法進行工作。
}
catch (Exception ex){
//如果發生例外,則進行相應處理。
};

----2、類java.lang.ClassLoader。這是一個抽象類,如果打算運用它,必須繼承它並重寫它的loadClass()方法。其主要方法有:

---- protected ClassLoader();

----這是一個建構元,可以用它建立一個ClassLoader類的實例。注意繼承這個類的類必須重寫這個方法,而不能使用缺省的建構元。

---- protected abstract Class loadClass(String name, boolean resolve)

----載入指定的類數據,建立Class類型的對象並根據需要解析它。這是一個抽象方法,大家必須在自己的子類中重寫這個方法,重寫的規則可以參考第三部分的例子。

---- protected final Class defineClass(byte data[], int offset, int length)

----將字節數組中的數據定義爲Class類型的對象,字節數組的格式由虛擬機規定。

---- protected final Class findSystemClass(String name)

----根據指定的類名載入類,它會自動在當前目錄和環境變量“CLASSPATH”指定的路徑中尋找,如果找不到,則會拋出ClassNotFoundException例外。

---- protected final void resolveClass(Class c)

----通過載入與指定的類相關的所有類來解析這個類,這必須在類被使用之前完成。

擴充ClasslLader類以實現動態類載入
----理解動態類載入機制的最好辦法是通過例子,下面這個完整的例子由四個類組成,分別解釋如下:

----1、MyClassLoader類是ClassLoader類的子類,它重寫了loadClass方法,實現了將網絡上用URL地址指定的類動態載入,取得它的Class類型對象的功能。讀者可根據自己載入類的具體方式改寫下面的代碼。

import wuhuif.io.*;
import wuhuif.util.*;
import wuhuif.net.*;

public class MyClassLoader extends ClassLoader {
//定義哈希表(Hashtable)類型的變量,
用於保存被載入的類數據。
Hashtable loadedClasses;

public MyClassLoader() {
loadedClasses = new Hashtable();
}

public synchronized Class loadClass(String className,
boolean resolve) throws ClassNotFoundException {
Class newClass;
byte[] classData;

//檢查要載入的類數據是否已經被保存在哈希表中。
newClass = (Class) loadedClasses.get(className);
//如果類數據已經存在且resolve值爲true,則解析它。
if (newClass != null){
if (resolve)
resolveClass(newClass);
return newClass;
}

----/*首先試圖從本地系統類組中載入指定類。這是必須的,因爲虛擬機將這個類載入後,在解析和執行它時所用到的任何其他類,如java.lang.System類等,均不再使用虛擬機的類載入器,而是調用我們自制的類載入器來加載。*/

try {
newClass = findSystemClass(className);
return newClass;
} catch (ClassNotFoundException e) {
System.out.println(className+" is not a system class!");
}

//如果不是系統類,
則試圖從網絡中指定的URL地址載入類。
try {
//用自定義方法載入類數據,
存放於字節數組classData中。
classData = getClassData(className);
//由字節數組所包含的數據建立一個class類型的對象。
newClass = defineClass(classData, 0, classData.length);
if (newClass == null)
throw new ClassNotFoundException(className);
} catch (Exception e) {
throw new ClassNotFoundException(className);
}

//如果類被正確載入,
則將類數據保存在哈希表中,以備再次使用。
loadedClasses.put(className, newClass);
//如果resolve值爲true,則解析類數據。
if (resolve){
resolveClass(newClass);
}
return newClass;
}
//這個方法從網絡中載入類數據。
protected byte[] getClassData(String className)
throws IOException {
byte[] data;
int length;
try {
//從網絡中採用URL類的方法
載入指定URL地址的類的數據。
URL url = new URL(className.endsWith(".class") ?
className : className + ".class");
URLConnection connection = url.openConnection();
InputStream inputStream = connection.getInputStream();
length = connection.getContentLength();

data = new byte[length];
inputStream.read(data);
inputStream.close();
return data;
} catch(Exception e) {
throw new IOException(className);
}
}
}

----2、由於Java是強類型檢查語言,通過網絡載入後的類被實例化後只是一個Object類型的對象,虛擬機並不知道它包含那些方法,應從哪個方法開始執行。因此,可以被動態載入的類必須繼承某一個抽象類或實現某一個接口,因爲父類只能有一個,所以通常用實現特定接口的辦法。下面的代碼定義了一個接口類share和它的方法start()。

public interface share {
public void start(String[] option);
}

----3、TestClassLoader類通過使用MyClassLoader類的loadClass()方法,將指定URL地址的類載入並在本地系統執行它,實現了類的動態載入。注意在執行被載入類的方法前一定要將它進行強制數據類型轉換。

public class TestClassLoader {
public static void main(String[] args){
MyClassLoader ll = new MyClassLoader();
Class cc;
Object oo;
String ss = "http://kyzser.ydxx/classLoader/Tested.class";

if (args.length != 0) ss = args[0];
try {
System.out.println("Loading class "+ss+"...");
//使用重寫的方法loadClass()載入類數據。
cc = ll.loadClass(ss);
System.out.println("Creat instance...");
//創建Object類型的類實例。
oo = cc.newInstance();
System.out.println("Call start() method...");
//強制類型轉換並執行被載入類中的方法。
((share) oo).start(args);
}catch (Exception e) {
System.out.println("Caught exception : "+e);
}
}
}

----4、Tested類很簡單,可以將它放在任何WEB服務器上,但應注意能動態載入且被執行的類,一定要實現預先定義的接口中的方法。下面的例子實現了接口share的start方法。

public class Tested implements share{
public void start(String[] option){
//填寫程序代碼。
}
}

動態類載入機制的幾點應用
----1、開發分佈式應用。這對開發遠程的客戶端應用程序最有用,客戶端僅需要安裝一些基本的系統和一個能實現動態類載入機制的類,需要本地系統不存在的功能時,僅需要從網絡動態載入並執行相應類即可獲得特定功能。因爲客戶端所使用的總是軟件的最新版本,所以不再存在軟件的升級和維護問題,即實現了所謂的“零管理”模式。

----2、對.class文件加密。由於Java的字節碼(bytecode)容易被反編譯,大部分開發Java應用程序的公司均擔心自己的成果被別人不勞而獲。其實可以將類文件進行適當的加密處理,執行時使用自己的類載入器進行相應的解密,就可以解決這個問題。

----3、使第三方開發者易於擴展你的應用。從前面可知,所有可以被你的類載入器動態載入並被執行的類,必須繼承你定義的類或實現你定義的接口,這樣,你可以制訂一些規則,使其他開發者不必瞭解你的應用程序也可以擴充功能。

----當然,有利必有弊,在網絡中使用動態類載入的主要缺陷在於安全性,很難保證不載入不懷好意的代碼,這個問題要靠Java的安全管理器和適當的加密算法來解決,已超出本文的討論範圍。 

發佈了47 篇原創文章 · 獲贊 2 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章