JVM類加載機制(三):自定義類加載器

自己編寫類加載器的意義

  • 當class文件不在ClassPath目錄下時婚苹,默認的系統(tǒng)類加載器無法找到該class文件,這時候需要自定義一個ClassLoader來加載特定路徑下的class對象
  • 當一個class文件需要通過網(wǎng)絡(luò)傳輸征绸,甚至可能會進行相應(yīng)的解密操作時祝拯,這時候需要編寫自定義的ClassLoader來實現(xiàn)相應(yīng)邏輯
  • 當要實現(xiàn)熱部署時(一個class文件通過不同的類加載器產(chǎn)生不同的class對象而實現(xiàn)熱部署功能),需要實現(xiàn)自定義的ClassLoader的邏輯

目標

本目錄針對以上三個意義,將分類實戰(zhàn)完成三類自定義ClassLoader的自定義實現(xiàn)财松,以便更好的掌握。

  • 自定義文件類加載器
  • 自定義網(wǎng)絡(luò)類加載器
  • 熱部署類加載器

自定義File類加載器

package classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * 從特定路徑的文件獲取類的File類加載器
 * @author XZP
 *
 */
public class FileClassLoader extends ClassLoader {
    private String rootDir;
    public FileClassLoader(String rootDir) {
        this.rootDir = rootDir;
    }
    /**
     * 重寫父類的findClass方法
     * @param name 文件名
     * @return 找到類的字節(jié)碼就用defineClass返回對應(yīng)的對象
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // 獲取類的class文件字節(jié)數(shù)組
        byte[] classData = getClassDataFromFile(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            // 直接生成class對象
            return defineClass(name, classData, 0, classData.length);
        }
    }
    /**
     * 獲取class文件并轉(zhuǎn)換為字節(jié)碼流的邏輯
     * @param name 文件名
     * @return
     */
    private byte[] getClassDataFromFile(String name) {
        // 讀取類文件的字節(jié)
        String path = name2Path(name);
        System.out.println("path:" + path);
        try {
            InputStream ins = new FileInputStream(path);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int bufferSize = 4096;
            byte[] buffer = new byte[bufferSize];
            int bytesNumRead = 0;
            // 讀取文件字節(jié)碼
            while ((bytesNumRead = ins.read(buffer)) != -1) {
                baos.write(buffer, 0, bytesNumRead);
            }
            return baos.toByteArray();
        } catch(IOException io) {
            io.printStackTrace();
        }
        return null;
    }
    /**
     * 通過文件名獲取class文件的完全路徑
     * @param className 文件名
     * @return
     */
    private String name2Path(String className) {
        return rootDir +  File.separatorChar + className.replace('.', File.separatorChar) + ".class";
    }
}

自定義網(wǎng)絡(luò)類加載器

package classloader;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;

public class NetClassLoader extends ClassLoader {
    private String url; // class文件所在的URL
    public NetClassLoader(String url) {
        this.url = url;
    }
    /**
     * 重寫父類的findClass方法
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = getClassDataFromNet(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            return defineClass(name,classData, 0, classData.length);
        }
    }
    /**
     * 從網(wǎng)絡(luò)獲取class文件,不包含解密
     * @param name
     * @return
     */
    private byte[] getClassDataFromNet(String name) {
        String path = name2Path(name);
        try {
            URL url = new URL(path);
            InputStream ins = url.openStream();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int bufferSize = 4096;
            byte[] buffer = new byte[bufferSize];
            int hasRead = 0;
            while ((hasRead = ins.read(buffer)) != -1) {
                baos.write(buffer, 0, hasRead);
            }
            // 可能還涉及解密,在此省略,但是是非常重要的一個內(nèi)容
            return baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 根據(jù)文件名得到文件URL
     * @param name
     * @return
     */
    private String name2Path(String name) {
        // 得到class文件的URL
        return url + "/"  + name.replace('.', '/') + ".class"; // 注意網(wǎng)絡(luò)路徑表示和File路徑表示還是有區(qū)別的
    }
}

熱部署類加載器

package classloader;
/**
 * 熱部署的測試類
 * 作如下特殊說明:
 * 熱部署就是利用同一個class文件不同的類加載器在內(nèi)存創(chuàng)建出兩個不同的class對象.
 * 由于JVM在加載類之前會檢測請求的類是否已加載過(即在loadClass()方法中調(diào)用findLoadedClass()方法),
 * 如果被加載過惫皱,則直接從緩存獲取捷绑,不會重新加載进苍。注意同一個類加載器的實例和同一個class文件只能被加載器一次宋下,多次加載將報錯.
 * 因此我們實現(xiàn)的熱部署必須讓同一個class文件可以根據(jù)不同的類加載器重復(fù)加載揭蜒,以實現(xiàn)熱部署:也即為同一個class文件寫兩個不同的類加載器
 * @author XZP
 *
 */
public class HotDeployTest {

    public static void main(String[] args) {
        String rootDir = "F:\\java_workspace\\jvm\\src\\classloader";
        // 創(chuàng)建自定義類的加載器:兩個不同的對象
        FileClassLoader loader1 = new FileClassLoader(rootDir);
        FileClassLoader loader2 = new FileClassLoader(rootDir);
        try {
            // 通過調(diào)用loadClass()加載指定的class文件
            Class<?> object1 = loader1.loadClass("classloader.FileResObj");
            Class<?> object2 = loader2.loadClass("classloader.FileResObj");
            System.out.println("obj1 by loadClass:" + object1.hashCode()); // 輸出的兩者hashCode相等葱峡,說明是同一對象
            System.out.println("obj2 by loadClass:" + object2.hashCode());
            // 通過調(diào)用findClass()方法繞過檢測,創(chuàng)建不同的class對象
            Class<?> object3 = loader1.findClass("FileResObj"); // 輸出的兩者hashCode不相等說明是不同對象
            Class<?> object4 = loader2.findClass("FileResObj");
            System.out.println("obj3 by findClass:" + object3.hashCode());
            System.out.println("obj4 by findClass:" + object4.hashCode());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市筐钟,隨后出現(xiàn)的幾起案子篓冲,更是在濱河造成了極大的恐慌琳拨,老刑警劉巖恶耽,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件负溪,死亡現(xiàn)場離奇詭異咙咽,居然都是意外死亡麸粮,警方通過查閱死者的電腦和手機溉苛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來弄诲,“玉大人愚战,你說我怎么就攤上這事∑胱瘢” “怎么了寂玲?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長梗摇。 經(jīng)常有香客問我拓哟,道長,這世上最難降的妖魔是什么伶授? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任断序,我火速辦了婚禮流纹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘违诗。我一直安慰自己漱凝,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布诸迟。 她就那樣靜靜地躺著茸炒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪阵苇。 梳的紋絲不亂的頭發(fā)上扣典,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音慎玖,去河邊找鬼贮尖。 笑死,一個胖子當著我的面吹牛趁怔,可吹牛的內(nèi)容都是我干的湿硝。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼润努,長吁一口氣:“原來是場噩夢啊……” “哼关斜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起铺浇,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤痢畜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鳍侣,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丁稀,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年倚聚,在試婚紗的時候發(fā)現(xiàn)自己被綠了线衫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡惑折,死狀恐怖授账,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情惨驶,我是刑警寧澤白热,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站粗卜,受9級特大地震影響屋确,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一乍恐、第九天 我趴在偏房一處隱蔽的房頂上張望评疗。 院中可真熱鬧,春花似錦茵烈、人聲如沸百匆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽加匈。三九已至,卻和暖如春仑荐,著一層夾襖步出監(jiān)牢的瞬間雕拼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工粘招, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留啥寇,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓洒扎,卻偏偏與公主長得像辑甜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子袍冷,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容