日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

Java程序员从笨鸟到菜鸟之(九十九)深入java虚拟机(八)开发自己的类加载器...

發(fā)布時(shí)間:2024/4/11 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java程序员从笨鸟到菜鸟之(九十九)深入java虚拟机(八)开发自己的类加载器... 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

? ? ??

歡迎閱讀本專題的其他博客:


  • ? ? ? ? ? ? ? ? ?????深入java虛擬機(jī)(一)——java虛擬機(jī)底層結(jié)構(gòu)詳解
  • ? ? ? ? ? ? ? ? ? ? ? ? ?深入java虛擬機(jī)(二)——類的生命周期(上)類的加載和連接
  • ? ? ? ? ? ? ? ? ? ? ? ? ?深入java虛擬機(jī)(三)——類的生命周期(下)類的初始化
  • ? ? ? ? ? ? ? ? ? ? ? ? ?深入java虛擬機(jī)(四)——java虛擬機(jī)的垃圾回收機(jī)制
  • ? ? ? ? ? ? ? ? ? ? ? ? ?深入java虛擬機(jī)(五)——java本地接口JNI詳解
  • ? ? ? ? ? ? ? ? ? ? ? ? ?深入java虛擬機(jī)(六)——類加載的父親委托機(jī)制
  • ? ? ? ? ? ? ? ? ? ? ? ? ?深入java虛擬機(jī)(七)深入源碼看java類加載器ClassLoader

? ? ? ? ? 在大多數(shù)情況下,系統(tǒng)默認(rèn)提供的類加載器實(shí)現(xiàn)已經(jīng)可以滿足需求。但是在某些情況下,您還是需要為應(yīng)用開發(fā)出自己的類加載器。比如您的應(yīng)用通過網(wǎng)絡(luò)來傳輸?Java?類的字節(jié)代碼,為了保證安全性,這些字節(jié)代碼經(jīng)過了加密處理。這個(gè)時(shí)候您就需要自己的類加載器來從某個(gè)網(wǎng)絡(luò)地址上讀取加密后的字節(jié)代碼,接著進(jìn)行解密和驗(yàn)證,最后定義出要在?Java?虛擬機(jī)中運(yùn)行的類來。下面將通過兩個(gè)具體的實(shí)例來說明類加載器的開發(fā)。

?

?????要?jiǎng)?chuàng)建自定義的類加載器只需要擴(kuò)展java.lang.ClassLoader類就可以,然后覆蓋它的findClass(String?name)方法即可。該方法根據(jù)參數(shù)指定的類的名字,去找對(duì)應(yīng)的class文件。然后返回class對(duì)應(yīng)的對(duì)象。下面我們就根據(jù)我們自定義的類加載器的源碼來具體詳解一下這個(gè)自定義的步驟:


自定義的類加載器:

package com.bzu.csh.test; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; /** * * @author 曹勝歡 * @version 1.0 * */ public class MyClassLoader extends ClassLoader { private String name; // 類加載器的名字 private String path = "d:\\"; // 加載類的路徑 private final String fileType = ".class"; // class文件的擴(kuò)展名 public MyClassLoader(String name) { super(); // 讓系統(tǒng)類加載器成為該類加載器的父加載器 this.name = name; } public MyClassLoader(ClassLoader parent, String name) { super(parent); // 顯式指定該類加載器的父加載器 this.name = name; } @Override public String toString() { return this.name; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } /** * @param 類文件的名字 * @return 類文件中類的class對(duì)象 * * 在這里我們并不需要去顯示的調(diào)用這里的findclass方法,在上篇文章中,我們通過查看 * loadclass的源碼可以發(fā)現(xiàn),她是在loadclass中被調(diào)用的,所以這里我們只需重寫這個(gè)方法, * 讓它根據(jù)我們的想法去查找類文件就ok,他會(huì)自動(dòng)被調(diào)用 * * * defineClass()將一個(gè) byte 數(shù)組轉(zhuǎn)換為 Class 類的實(shí)例。必須分析 Class,然后才能使用它 * 參數(shù): * name - 所需要的類的二進(jìn)制名稱,如果不知道此名稱,則該參數(shù)為 null * b - 組成類數(shù)據(jù)的字節(jié)。off 與 off+len-1 之間的字節(jié)應(yīng)該具有《Java Virtual Machine Specification》定義的有效類文件的格式。 * off - 類數(shù)據(jù)的 b 中的起始偏移量 * len - 類數(shù)據(jù)的長度 */ @Override public Class<?> findClass(String name) throws ClassNotFoundException { byte[] data = this.loadClassData(name);//獲得類文件的字節(jié)數(shù)組 return this.defineClass(name, data, 0, data.length);// } /** * * @param 類文件的名字 * @return 類文件的 字節(jié)數(shù)組 * 通過類文件的名字獲得類文件的字節(jié)數(shù)組,其實(shí)主要就是用 * 輸入輸出流實(shí)現(xiàn)。 */ private byte[] loadClassData(String name) { InputStream is = null; byte[] data = null; ByteArrayOutputStream baos = null; try { this.name = this.name.replace(".", "\\"); is = new FileInputStream(new File(path + name + fileType)); baos = new ByteArrayOutputStream(); int ch = 0; while (-1 != (ch = is.read())) { baos.write(ch); } data = baos.toByteArray(); } catch (Exception ex) { ex.printStackTrace(); } finally { try { is.close(); baos.close(); } catch (Exception ex) { ex.printStackTrace(); } } return data; }

? ? ? ?我想上面的注釋中已經(jīng)足夠讓大家明白這個(gè)自定義類加載器的原理了。在這我在重復(fù)的從上到下的再說一遍,加深一下大家的理解。首先在構(gòu)造方法中,我們可以通過構(gòu)造方法給類加載器起一個(gè)名字,也可以顯示的指定他的父累加器器,如果沒有顯示的指出父類加載器的話他默認(rèn)的就是系統(tǒng)類加載器。由于我們繼承了ClassLoader類,所以它自動(dòng)繼承了父類的loadclass方法。我們以前看過loadclass的源碼知道,它調(diào)用了findclass方法去查找類文件。所以在這里我們重寫了ClassLoaderfindclass方法。在這個(gè)方法中首先調(diào)用loadClassData方法,通過類文件的名字獲得類文件的字節(jié)數(shù)組,其實(shí)主要就是用輸入輸出流實(shí)現(xiàn)。然后調(diào)用defineClass()將一個(gè)?字節(jié)?數(shù)組轉(zhuǎn)換為?Class?類的實(shí)例。有時(shí)候我們手動(dòng)生成的二進(jìn)制碼的class文件被加密了。所以在我們?cè)诶梦覀冏远x的類加載器的時(shí)候還要寫一個(gè)解密的方法進(jìn)行解密,這里我們就不實(shí)現(xiàn)了。


我們實(shí)現(xiàn)了自定義類加載器,下一步我們來看一下我們?cè)趺磥響?yīng)用我們這個(gè)自定義的類加載器:


public static void main(String[] args) throws Exception { //創(chuàng)建一個(gè)loader1類加載器,設(shè)置他的加載路徑為d:\\serverlib\\,設(shè)置默認(rèn)父加載器為系統(tǒng)類加載器 MyClassLoader loader1 = new MyClassLoader("loader1"); loader1.setPath("d:\\myapp\\serverlib\\"); //創(chuàng)建一個(gè)loader2類加載器,設(shè)置他的加載路徑為d:\\clientlib\\,并設(shè)置父加載器為loader1 MyClassLoader loader2 = new MyClassLoader(loader1, "loader2"); loader2.setPath("d:\\myapp\\clientlib\\"); //創(chuàng)建一個(gè)loader3類加載器,設(shè)置他的加載路徑為d:\\otherlib\\,并設(shè)置父加載器為根類加載器 MyClassLoader loader3 = new MyClassLoader(null, "loader3"); loader3.setPath("d:\\myapp\\otherlib\\"); test(loader2); System.out.println("----------"); test(loader3); } public static void test(ClassLoader loader) throws Exception { Class clazz = loader.loadClass("com.bzu.csh.test.Sample"); Object object = clazz.newInstance(); }


? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 類加載器結(jié)構(gòu)圖

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

? ? ? ? ? ? ? ? ? ? ? (PS:突然發(fā)現(xiàn)WPS自帶的畫圖工具也挺好用,雖然有點(diǎn)難看,哈哈)


? ? ? ? ? ?當(dāng)執(zhí)行這段代碼的時(shí)候。首先讓loader2去加載Sample類文件,當(dāng)然我們?cè)趫?zhí)行這段代碼的前提時(shí)在各個(gè)默認(rèn)加載器中已經(jīng)有我們Sampleclass文件。Loader2首先讓父加載器是loader1去加載,然后loader1會(huì)讓系統(tǒng)類加載器去加載,系統(tǒng)類加載器會(huì)讓擴(kuò)展類加載器加載,擴(kuò)展類加載器會(huì)讓根類加載器加載,由于系統(tǒng)類加載器,擴(kuò)展類加載器,根類加載器的默認(rèn)路徑中都沒有我們要的sample類,所以loader2的默認(rèn)路徑有sample這個(gè)類,也就是說loader2會(huì)去加載這個(gè)sample類。當(dāng)執(zhí)行testloader3)的時(shí)候,由于loader3的默認(rèn)父加載器是根類加載器,并且根類加載前默認(rèn)路徑?jīng)]有對(duì)應(yīng)的sample.class文件,所以,直接的loader3類加載器就去加載這個(gè)類。

? ? ? ? 最后要說明的一點(diǎn)是,自定義類加載不光只能從我們本地加載到class文件,我們也可以加載網(wǎng)絡(luò),即基本的場景是:Java?字節(jié)代碼(.class)文件存放在服務(wù)器上,客戶端通過網(wǎng)絡(luò)的方式獲取字節(jié)代碼并執(zhí)行。當(dāng)有版本更新的時(shí)候,只需要替換掉服務(wù)器上保存的文件即可。通過類加載器可以比較簡單的實(shí)現(xiàn)這種需求。其實(shí)他的實(shí)現(xiàn)和本地差不多,基本上就是geclassdata方法改變了一些。下面我們來具體看一下:

private byte[] getClassData(String className) { String path = classNameToPath(className); try { URL url = new URL(path); InputStream ins = url.openStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int bufferSize = 4096; byte[] buffer = new byte[bufferSize]; int bytesNumRead = 0; while ((bytesNumRead = ins.read(buffer)) != -1) { baos.write(buffer, 0, bytesNumRead); } return baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } return null; }

? ? ? ?在通過網(wǎng)絡(luò)加載了某個(gè)版本的類之后,一般有兩種做法來使用它。第一種做法是使用?Java?反射?API。另外一種做法是使用接口。需要注意的是,并不能直接在客戶端代碼中引用從服務(wù)器上下載的類,因?yàn)槲覀儗懙念惣虞d器被加載所用的類加載器和我們加載的網(wǎng)絡(luò)類不是同一個(gè)類加載器,所以客戶端代碼的類加載器找不到這些類。使用?Java?反射?API?可以直接調(diào)用?Java?類的方法。而使用接口的做法則是把接口的類放在客戶端中,從服務(wù)器上加載實(shí)現(xiàn)此接口的不同版本的類。在客戶端通過相同的接口來使用這些實(shí)現(xiàn)類。

??

不同類加載器的命名空間關(guān)系:


1.同一個(gè)命名空間內(nèi)的類是相互可見的。

2.子加載器的命名空間包含所有父加載器的命名空間。因此子加載器加載的類能看見父加載器加載的類。例如系統(tǒng)類加載器加載的類能看見根類加載器加載的類。

3.由父加載器加載的類不能看見子加載器加載的類。

4.如果兩個(gè)加載器之間沒有直接或間接的父子關(guān)系,那么它們各自加載的類相互不可見。

5.當(dāng)兩個(gè)不同命名空間內(nèi)的類相互不可見時(shí),可以采用Java的反射機(jī)制來訪問實(shí)例的屬性和方法。

------------------------------------------------------------------------------------------------------------------------
? 廣告:我參加了2012年度IT博客大賽,希望大家能多多支持

https://blog.51cto.com/contest2012/3545281

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

轉(zhuǎn)載于:https://blog.51cto.com/javacsh/1129126

總結(jié)

以上是生活随笔為你收集整理的Java程序员从笨鸟到菜鸟之(九十九)深入java虚拟机(八)开发自己的类加载器...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。