JVM 類加載器(5)自定義類加載器

core-java.jpg

類加載器算是 JVM 的核心技術(shù),也是比較難懂的知識(shí),因此當(dāng)然也就是成為了 java 面試中不可少內(nèi)容,或多或少都會(huì)問(wèn)到一個(gè)兩個(gè)有關(guān) classLoader 的問(wèn)題。所以作為 java developer 我們應(yīng)該吃透這部分內(nèi)容,才能在編程之路上有所突破。

類加載器,顧名思義是加載類,也就是將類放到一個(gè)位置供 JVM 使用,進(jìn)一步具體點(diǎn)就是將讀取 .class 后綴文件中內(nèi)容(內(nèi)容就是字節(jié)碼形式存儲(chǔ)的,字節(jié)碼的本質(zhì)就是一個(gè)字節(jié)數(shù)組 []byte,有特定的復(fù)雜的內(nèi)部格式。)讀取出來(lái)然后以 class 形式保存在內(nèi)存中共 JVM 使用。

懶加載
java 的 classLoader 也是按需加載,而不是一下子將所有的類都加載到 JVM。明顯的實(shí)例就是在調(diào)用一個(gè)類的靜態(tài)方法時(shí)候,只會(huì)加載這個(gè)類靜態(tài)資源而不會(huì)加載這個(gè)類實(shí)例的資源。

雙親委派
我們不用關(guān)心字面意思,首先知道他是一個(gè)概念就行,就是面試提問(wèn)字面量,了解其背后含義才能讓我們更好了解 JVM 加載類機(jī)制。在 Java 中有 3 種類加載器,分別是引導(dǎo)類加載器,擴(kuò)展類加載器和系統(tǒng)類加載器。三者是的關(guān)系是:引導(dǎo)類加載器是擴(kuò)展類加載器的父類,擴(kuò)展類加載器是系統(tǒng)類加載器的父類。雙親(父類)委派模型: 某加載器 每次準(zhǔn)備加載類的時(shí)候,都會(huì)先嘗試委托(其父類)加載器進(jìn)行加載該類。

BootstrapClassLoader 負(fù)責(zé)加載 JVM 運(yùn)行時(shí)核心類,這些類位于 JAVA_HOME/lib/rt.jar 文件中,我們常用內(nèi)置庫(kù) java.xxx.* 都在里面,比如 java.util.、java.io.、java.nio.、java.lang. 等等。這個(gè) ClassLoader 比較特殊,是由 C 代碼實(shí)現(xiàn)的,將其稱之為根加載器

public class ZClientC {
    public static void main(String[] args) {
        URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
        for (int i = 0; i < urls.length; i++) {
            URL url = urls[i];
            System.out.println(url.toExternalForm());
        }
    }
}

file:/C:/Program%20Files/Java/jdk1.8.0_171/jre/lib/resources.jar
file:/C:/Program%20Files/Java/jdk1.8.0_171/jre/lib/rt.jar
file:/C:/Program%20Files/Java/jdk1.8.0_171/jre/lib/sunrsasign.jar
file:/C:/Program%20Files/Java/jdk1.8.0_171/jre/lib/jsse.jar
file:/C:/Program%20Files/Java/jdk1.8.0_171/jre/lib/jce.jar
file:/C:/Program%20Files/Java/jdk1.8.0_171/jre/lib/charsets.jar
file:/C:/Program%20Files/Java/jdk1.8.0_171/jre/lib/jfr.jar
file:/C:/Program%20Files/Java/jdk1.8.0_171/jre/classes

ExtensionClassLoader 負(fù)責(zé)加載 JVM 擴(kuò)展類,比如 swing 系列、內(nèi)置的 js 引擎、xml 解析器 等等,這些庫(kù)名通常以 javax 開頭,它們的 jar 包位于 JAVA_HOME/lib/ext/*.jar 中,有很多 jar 包。

System.out.println(System.getProperty("java.ext.dirs"));
        ClassLoader extensionClassloader=ClassLoader.getSystemClassLoader().getParent();
        System.out.println("the parent of extension classloader : "+extensionClassloader.getParent());

AppClassLoader 才是直接面向用戶的加載器,會(huì)加載 Classpath 環(huán)境變量里定義的路徑中的 jar 包和目錄。我們自己編寫的代碼以及使用的第三方 jar 包通常都是由AppClassLoader 來(lái)加載的。

th.jpg

當(dāng)然我們可以自己寫一個(gè) classloader 在加載類,通過(guò) classLoader 過(guò)程對(duì)上面的內(nèi)容結(jié)合實(shí)際理解一下。這樣就不會(huì)顯得那么枯燥和抽象了。

class MClassLoader extends ClassLoader{
    private String className = "";
    
    
}

public class ZClientE {
    public static void main(String[] args) {

    }
}

首先需要復(fù)寫一下 findClass 這個(gè)方法,這個(gè)方法接收 class 名稱作為參數(shù),返回一個(gè)類。

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        return super.findClass(name);
    }

感興趣可以查看 findClass 的源碼,這個(gè)方法就是拋出了一個(gè)異常。

protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }
private byte[] loadClassData(String className){
        InputStream is = getClass().getClassLoader().getResourceAsStream(className.replace(".","http://") + classExtensionName);
        ByteArrayOutputStream byteStr = new ByteArrayOutputStream();

        int len = 0;
        try {
            while((-1 != (len = is.read()))){
                byteStr.write(len);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        return byteStr.toByteArray();

    }

這個(gè)方法負(fù)責(zé)通過(guò)讀取 class 文件來(lái)返回字節(jié)碼數(shù)組同 findClass 方法使用。每一個(gè)類都有一個(gè)類加載器。在程序運(yùn)行時(shí)當(dāng)遇到一個(gè)未知的類,JVM 是如何加載這個(gè)類的呢?使用調(diào)用者(調(diào)用這個(gè) Class)的對(duì)象的 Class 。getClass().getClassLoader() 獲取類的類加載器讀取文件的字節(jié)碼數(shù)組。然后將其字節(jié)碼數(shù)組作為返回值供 findClass 使用

定義一個(gè)類作為加載目標(biāo)類 Greet ,簡(jiǎn)單的 show 方法來(lái)打印一條信息。

package com.zidea.test;

public class Greet {
    public void show(){
        System.out.println("hello classloader...");
    }
}

在 main 方法調(diào)用我們自己類加載器來(lái)加載類

public static void main(String[] args) {
        MClassLoader loader = new MClassLoader();

        try {
            Class<?> greet = loader.findClass("com.zidea.test.Greet");
            Object ob = greet.newInstance();
            Method md = greet.getMethod("show");
            md.invoke(ob);
        } catch (Exception var5) {
            var5.printStackTrace();
        }

    }

這是自定義 ClassLoader 完整代碼,

class MClassLoader extends ClassLoader{
    private String className = "";
    private final String classExtensionName = ".class";

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] bytes = loadClassData(name);

        return defineClass(name,bytes,0,bytes.length);
    }

    private byte[] loadClassData(String className){
        InputStream is = getClass().getClassLoader().getResourceAsStream(className.replace(".","http://") + classExtensionName);
        ByteArrayOutputStream byteStr = new ByteArrayOutputStream();

        int len = 0;
        try {
            while((-1 != (len = is.read()))){
                byteStr.write(len);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        return byteStr.toByteArray();

    }
}
loader.jpg
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容