亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Class Loading ---(類裝載機制,開發者不得

系統 1812 0

http://blog.csdn.net/totodo/archive/2005/01/28/271798.aspx

也許你認為Class Load是一個高級話題,不管怎樣,作為開發者你還是要了解它。

本文基于最新得JDK5,然而將訴的內容卻包含了最基本的原理,希望你能更加深入了解自己所使用得語言。
?
?

理解CLassLoader
?
如果你自己定義了一個 org.test.Object 。
你在程序中這樣寫:
import ort.test.Object
Object o = new String();?
也許你欣然以為這樣寫沒問題,但實際上你錯了。?
這樣會報 ClassCastException ,
一個Class在JVM中得標識是由它得 PackAge 和 類名決定得(也就是它的名稱空間)所決定的。
?org.test.Object?并不等同于? java.lang.Object
?
在java中,每個類都是java.lang.Class得實例。所有的類都可以這樣自定義:
java.lang.Class klass = Myclass.class;
      
        
          而實例化一個類,可以是:Myclass myclass = new Myclass() 也可以是: myclass.newInstance();
        
      
    
?
?
在JVM中。所有得類都由 java.lang.ClassLoader .以及它的子類加載的,我們在運行程序的時候,首先要從 JAVA_HOME/jre/rt.jar開始。? 不過我們發現JDK文檔里并沒有介紹bootstrap.jar。 實際上bootstrap 是JDK之外得,它得方式和JVM的是不一樣的。

JDK中除了ClassLoader可以加載類之外,還有以下這些也可以。
  • java.net.URLClassLoader
  • java.security.SecureClassLoader
  • java.rmi.server.RMIClassLoader
  • sun.applet.AppletClassLoader
Class Loaders工作原理
?
? 除了bootstrap之外,所有的Classloader都有個父類Class Loader ,他們都是instanceof java.lang.ClassLoader
?
看看JDK1.5中的一個例子
假如有個方法loadClass
?
protected synchronized Class<?> loadClass
??? (String name, boolean resolve)
??? throws ClassNotFoundException{

??? // First check if the class is already loaded
??? Class c = findLoadedClass(name);
??? if (c == null) {
??????? try {
??????????? if (parent != null) {
??????????????? c = parent.loadClass(name, false);
??????????? } else {
??????????????? c = findBootstrapClass0(name);
??????????? }
??????? } catch (ClassNotFoundException e) {
??????????? // If still not found, then invoke
??????????? // findClass to find the class.
??????????? c = findClass(name);
??????? }
??? }
??? if (resolve) {
??? resolveClass(c);
??? }
??? return c;
}

?
那設置它父類的方式有兩種。
public class MyClassLoader extends ClassLoader{
??? public MyClassLoader(){
??????? super(MyClassLoader.class.getClassLoader());
??? }
}
或者
public class MyClassLoader extends ClassLoader{
??? public MyClassLoader(){
??????? super(getClass().getClassLoader());
??? }
}
?
這里是首選第一種。
因為getClass()是在構造函數內部得方法,所以必須要 有構造函數代碼存在,但是如果不存在,那就找父類得classloader 一直往上找,直至找到到findBootstrapClass, 如果它也不存在得話,那時候findClass()方法會被調用執行。。 (那時候會報一個ClassNotFoundException)
?
來看看findClass()得代碼

??? protected Class<?> findClass(String name)
??????? throws ClassNotFoundException {
??????? throw new ClassNotFoundException(name);
??? }
?
在findClass() 方法內 class loader要取得到得字節碼(就是編譯后*.class文件里得內容),也不一定就是.class文件, 這些字節碼可以來自本地,也可以是系統,網絡(借著這個你可以理解一下Cobra,RMI),也可以是用 BCEL (Apache一個基于字節碼得一個引擎庫) ...等等。 一但字節碼找到了。那時候就開始執行 defineClass()方法。那時候ClassLoader便會定義出一個類來。
?
每個ClassLoader出來的類都是不同的, 如果有兩個ClassLoader載入兩各相同的程序,defineClass()定義得兩個類也是不同得。詳細請看(
Java language specification
?
?
?
?
下面有幅圖畫 展示了一個MyMainclass.class是如何裝載執行的。(由多個classLoader加載同一個Target.class),----
根據上面得解析,既然由兩個classLoader()載入Target.class得字節碼 ,那defineClass()就會產生兩個class的定義。

?
所以很容易得出以下結論:
Target target1 = (Target) target2;?是不正確的?。target1 和 target2是由兩個不同的classloader定義的。
?
?
?
?
具體請看 Inside Class Loaders (Andreas Schaefer)




?
?我們是否需要自定義的ClassLoader?
?
??? 理由之一: 如果我們自定義了ClassLoader,那我們便可以控制JVM的加載動作了。
??
?? 上面說一個class標識是由于package+classname組成得。 對于所有實現java.io. Serializable 接口的類,都是由 serialVersionUID 管理這些類得版本( RMI,JNDI,Security里都有這樣一個ID) 。它用64位的Hash來表示 (這個Hash由classname,filed,method組成)。從技術上講如果classname,field,mehtod所構成的Hash都一樣,那就會認為是同一個版本。
? 假設有這樣一個情況,我們要寫一個java 執行引擎(比如:用一個RMI 發布一個Server端程序,執行client的接口方法) , ?既然要能執行,那引擎肯定要實現有Client所特定任務的接口(這里為TaskIntf)。? 一但任務提交給執行引擎,Server要做的第一件事情就是要裝載所有要執行的代碼。 假設不同的終端遞交了不同的代碼。而偏偏又都是同樣的包名,和同樣的類名。 那服務器能否會辨別到底是那個Client提交過來的執行請求?
?
? 現在出個問題: 如果在服務器端一個執行程序執行兩個客戶端提交同一個版本得代碼,如何才讓客戶端會得到預期的執行結果??
? 別以為這個很簡單,下面先建個RMI玩玩??纯唇Y果會是怎樣。
?
?
? 本地文件如下圖。
?
?
圖?2 程序目錄結構 (本文中含代碼)。
?
在samepath目錄下, 有著兩個version.Version.class,他們得包名類名都一樣,唯一不同的是。
v1目錄中的方法是:
??? public void fx(){
??????? log("this = " + this + "; Version.fx(1).");
??? }
v2目錄中的方法是:
??? public void fx(){
??????? log("this = " + this + "; Version.fx(2).");
??? }
?
執行一下看看:
set CLASSPATH=.;%CURRENT_ROOT%\v1;%CURRENT_ROOT%\v2
%JAVA_HOME%\bin\java Test 結果如下圖

Class Loading ---(類裝載機制,開發者不得不知道的故事)
圖?3. classPath得目錄設為v1

?
切換到
set CLASSPATH=.;%CURRENT_ROOT%\v2;%CURRENT_ROOT%\v1
%JAVA_HOME%\bin\java Test
結果如下圖:

Class Loading ---(類裝載機制,開發者不得不知道的故事)
圖?4. classpath目錄設為v2

?

很明顯,上面的例子中能從classpath中找到先后次序。如果我們把v1,v2的version.Version。都刪調。而把他們打成一個 myextension.jar包,放到java.ext.dirs目錄下。。這時候就通過 ExtClassLoader 來裝載了,而不是AppClassLoader.

結果會是如下:

Class Loading ---(類裝載機制,開發者不得不知道的故事)
圖?5. AppClassLoader and ExtClassLoader

注意看 sun.misc.Launcher$ExtClassLoader@a9c85c 這說明是ExtClassLoader 加載了。

?

繼續往下看,另外一個例子。?在 differentversions 目錄下的例子,里面包含了RMI的ServerImpl這樣一個執行引擎。Client實現了 common.TaskIntf接口。 兩個 client.TaskImpl分別如下:

??? static{
??????? log("client.TaskImpl.class.getClassLoader
??????? (v1) : " + TaskImpl.class.getClassLoader());
??? }

??? public void execute(){
??????? log("this = " + this + "; execute(1)");
??? }

?另一個則是:

???? ?static{
??????? log("client.TaskImpl.class.getClassLoader
??????? (v1) : " + TaskImpl.class.getClassLoader());
??? }

??? public void execute(){
??????? log("this = " + this + "; execute(2)");
??? }

這樣子來執行(順序隨便,這里把 %CURRENT_ROOT%\client2放在前面 ):

CLASSPATH=%CURRENT_ROOT%\common;%CURRENT_ROOT%\server;
??? %CURRENT_ROOT%\client2;%CURRENT_ROOT%\client1
%JAVA_HOME%\bin\java server.Server

先啟動Server..

? 分別把兩個client提交給服務器執行, (即便執行程序中得client1.bat 和 client2.bat server監控屏幕如圖6所示。)?

Class Loading ---(類裝載機制,開發者不得不知道的故事)
圖?6. Execution Engine Server console

?

再來看下面兩個圖(圖7和圖8),分別是client端得執行顯示。

Class Loading ---(類裝載機制,開發者不得不知道的故事)
圖?7. Execution Engine Client 1 console


Class Loading ---(類裝載機制,開發者不得不知道的故事)
圖?8. Execution Engine Client 2 console

?

縱觀上面三次執行結果,發現由于服務器啟動得時候使用了AppClassLoader.所以無論怎么樣都是載入得是client2(因為client2的classpath次序比較在前),

這里client1 很郁悶,它在自己那執行明明是? execute(1) 通過 RMI 發送給服務器端執行就成了 execute(2)..

值得注意的是: 在client1,client2分別發送給服務器執行之后,服務器端顯示的記錄是:
client.TaskImpl.class.getClassLoader(v2):sun.misc.lancuher@AppClassLoader@xxxx zhiz只執行了一次。而
this=client.TaskImpl@xxxxx execute(2);執行了兩次

上面已經講到過了,對于一個ClassLoader來講 同樣的page+className 只能定義一個 class,而不同的ClassLoader即便加載同一個page.className 也會定義不同的class

?

到這里,我才發現,解決上面提出得那個問題似乎并不容易。:(。

那如何解決呢?答案就是---使用自定義得classLoader ..

如果各位等不急的話, 先請看(目錄中 differentversionspush 里面的代碼)

很顯然,我們很有必要寫自定義的classloader.


如何構造使用自定義的ClassLoader

既然自定義的ClassLoader,能解決上述問題,那接下去看看,我們如何來使用自定義的ClassLoader。

結合本文種的原碼---(在differentversionspush的目錄里),有個FileSystemClassLoader,類圖描述如下:

Class Loading ---(類裝載機制,開發者不得不知道的故事)
圖9.

?

看看他的方法 findClassBytes(String className);

??? public byte[] findClassBytes(String className){

??????? try{
??????????? String pathName = currentRoot +
??????????????? File.separatorChar + className.
??????????????? replace('.', File.separatorChar)
??????????????? + ".class";
??????????? FileInputStream inFile = new
??????????????? FileInputStream(pathName);
??????????? byte[] classBytes = new
??????????????? byte[inFile.available()];
??????????? inFile.read(classBytes);
??????????? return classBytes;
??????? }
??????? catch (java.io.IOException ioEx){
??????????? return null;
??????? }
??? }

??? public Class findClass(String name)throws
??????? ClassNotFoundException{

??????? byte[] classBytes = findClassBytes(name);
??????? if (classBytes==null){
??????????? throw new ClassNotFoundException();
??????? }
??????? else{
??????????? return defineClass(name, classBytes,
??????????????? 0, classBytes.length);
??????? }
??? }

??? public Class findClass(String name, byte[]
??????? classBytes)throws ClassNotFoundException{

??????? if (classBytes==null){
??????????? throw new ClassNotFoundException(
??????????????? "(classBytes==null)");
??????? }
??????? else{
??????????? return defineClass(name, classBytes,
??????????????? 0, classBytes.length);
??????? }
??? }

??? public void execute(String codeName,
??????? byte[] code){

??????? Class klass = null;
??????? try{
??????????? klass = findClass(codeName, code);
??????????? TaskIntf task = (TaskIntf)
??????????????? klass.newInstance();
??????????? task.execute();
??????? }
??????? catch(Exception exception){
??????????? exception.printStackTrace();
??????? }
??? }

這 個類FileSystemClassLoader 被client使用了,用來定義class, 并且把它把client.TaskImpl(v1)轉化為 byte[], 然后 byte[]發送到RMI Server執行。(上面講了defineClass()能夠執行任何字節碼,來自編譯后的文件,網絡甚至是BCEL 字節碼引擎庫),?? 在Server端 ,又可以通過FileSystemClassLoader 以為byte[]的形式定義出 client.TaskImpl。

?

請看Client端的代碼:

public class Client{

??? public static void main (String[] args){

??????? try{
??????????? byte[] code = getClassDefinition
??????????????? ("client.TaskImpl");
??????????? serverIntf.execute("client.TaskImpl",
??????????????? code);
??????????? }
??????????? catch(RemoteException remoteException){
??????????????? remoteException.printStackTrace();
??????????? }
??????? }

??? private static byte[] getClassDefinition
??????? (String codeName){
??????? String userDir = System.getProperties().
??????????? getProperty("BytePath");
??????? FileSystemClassLoader fscl1 = null;

??????? try{
??????????? fscl1 = new FileSystemClassLoader
??????????????? (userDir);
??????? }
??????? catch(FileNotFoundException
??????????? fileNotFoundException){
??????????? fileNotFoundException.printStackTrace();
??????? }
??????? return fscl1.findClassBytes(codeName);
??? }
}

在RMI服務器端ServerImpl?程序里,?接受到來自client的字節碼(byte[]),于是FileSystemClassLoader 會從byte[]構造出一個class, 實例話,并且執行。

有一點要注意:每次接收到一個client的請求,FileSystemClassLoader都會重新實例化(執行結果中可以看出來),這就意 味著,client.Impl不在是在classpath中被找到的,而是通過FileSystemClassLoader 的findClass() 來執行deFineClass(),這樣每次 FileSystemClassLoader?都是創建新的實例,,自然 deFine出來的class也是不同的。 這樣,我們就能在RMI的執行中區分出 這兩個class來。(client.TaskImpl != client.TaskImp??在上篇就已經得出結論了。?)

看看服務器端的執行代碼:

public void execute(String codeName, byte[] code)throws RemoteException{

??????? FileSystemClassLoader fileSystemClassLoader = null;

??????? try{
??????????? fileSystemClassLoader = new FileSystemClassLoader();
??????????? fileSystemClassLoader.execute(codeName, code);
??????? }
??????? catch(Exception exception){
??????????? throw new RemoteException(exception.getMessage());
??????? }
??? }

?

服務器端的執行結果:

Class Loading ---(類裝載機制,開發者不得不知道的故事)
圖10,服務器端顯示

下面兩圖分別是客戶端顯示的。

Class Loading ---(類裝載機制,開發者不得不知道的故事)
圖11. client1的執行顯示


Class Loading ---(類裝載機制,開發者不得不知道的故事)
圖12. client2執行結果

?

哈,上面洋洋灑灑那么多,總算是一步一步的教會了大家 如何在同一個VM虛擬機中,執行“不同版本”的代碼 。(這些代碼有同樣的類名和包名)。

?

Class Loaders 在 J2EE 中應用。

到這里你其實已經不足為奇下面一些東西了。。。
????? 我的一個A_war.war的web項目中 代碼是 com.mycom.Test 而我在另外一個B_war.war的wenb項目中的 代碼也是com.mycom.Test 而他們照樣工作的好好的。
????? 當一個大型的 EJB項目,一臺服務器上部署了多個 EJB,War工程時候,他們也不會互相影響。AppServer還會有自己的裝載策略,比如你web中用的jar包,會優先于AppServer本身所帶有的。

原文:
http://www.onjava.com/pub/a/onjava/2005/01/26/classloading.htm

Class Loading ---(類裝載機制,開發者不得不知道的故事)


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦?。?!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 玖玖在线 | 久久国产热这里只有精品8 久久国产三级 | 在线播放人成午夜免费视频 | 中文字幕在线观看第二页 | 精品综合网 | 亚洲美女视频网站 | 香蕉视频在线观看免费 | 99re热精品视频国产免费 | 99热久久精品国 | 热久久亚洲 | 色成年激情久久综合 | se在线观看 | 中文字幕日韩在线观看 | 久久国产欧美另类久久久 | 黄色在线视频网 | 亚洲国产精品ⅴa在线观看 亚洲国产精品aa在线看 | 摸逼综合网 | 乱人伦视频69 | 免费观看黄色小视频 | 欧美日本中文字幕 | 真人实干一级毛片aa免费 | 午夜一级影院 | 不卡久久 | 久久天天躁夜夜躁狠狠躁2020 | 狠狠躁夜夜躁人人爽天天不 | 欧美精品成人a多人在线观看 | 2020年国产高中毛片在线视频 | 日本一级毛片在线观看 | 亚洲精品国产精品一区二区 | 久久99热久久精品 | 女人一级一级毛片 | 婷婷色在线视频 | 特黄特色一级aa毛片免费观看 | 奇米影视狠狠狠天天777 | 久久久噜噜噜久久老司机 | 91破解版在线 | 亚洲 | 黄页网站 播放器 日本 | 国产二区精品视频 | 操操碰| 久久久久久人精品免费费看 | 伊人五月天婷婷琪琪综合 |