為什么JVM需要多種類加載器


JVM的類加載器

剛剛學習JVM的類加載機制的時候,會被教育說JVM的類加載機制需要不同的類加載器。過了很久之后忘記了。現在再復習一下。

為什么需要多個類加載器?
加載器作用是通過類名來獲取二進制字節流。

我們先拋開所有問題,從寫程序的角度來講一個程序應該具有什么?
1、健壯性
2、功能性
3、魯棒性
4、效率性
5、維護性
6、可靠性(安全性)

由此,我們對比JVM。JVM也是一個軟件,也應該基本符合上述的幾個特性。
健壯性和功能性:JVM可以從不同的地方去加載class,比如文件系統,web,FTP等,這就要求JVM屏蔽底層的加載邏輯,只需要提供一個classloard()的接口就行了,客戶端就可以加載類但是卻不用管類加載器到底是怎么實現的。
再說安全性:JVM得保證自有類不遭到破壞(比如java.lang包下的類,這個破壞的原理就是類對同包級別及其子包下的public類具有操作權限)。為了解決這個問題,使用雙親委派機制剛好可以解決。

此外,如果我想在一個JVM中啟動兩個相同的應用的不同版本(不同版本值得是同樣的全限定名的類,但是功能不一樣),怎么辦?假設只有一個類加載器,那么第二次加載該類會失敗,第二個版本的應用還是使用了第一個版本的class,那么第二個應用就無法提供第二個版本類的功能了。所以得需要每個應用都有相互隔離的類加載器,否則第二個應用的類可能會覆蓋第一個應用之前加載的類,從而造成一些意想不到的后果。

每種加載器都有對應的層級來加載某些特定的類,來保證他們之間的安全性。

自定義的類加載器。

package com.stat;

import java.util.Date;

public class MyDate extends Date {
    @Override
    public String toString() {
        return "MyDate : 我的時間。";
    }
}
package com.stat;

import java.io.*;

/**
 * 自定義類加載器
 */
public class MyClassLoader extends ClassLoader {
    String classDir;

    public MyClassLoader() {
    }

    public MyClassLoader(String classDir) {
        this.classDir = classDir;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String classFile = classDir + "/" + name + ".class";
        System.out.println("classFile path==" + classFile);
        try {
            //這個地方我們只是簡單的讀取文件流的方式來獲取byte數組
            //其實可以嘗試將class文件加密以后 這里解密 這樣就可以保證
            //這種class文件 只有你寫的classloader才能讀取的了。
            //其他任何classloader都讀取不了 包括系統的。
            byte[] classByte = toByteArray(classFile);
            return defineClass(classByte, 0, classByte.length);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return super.findClass(name);
    }

    public static byte[] toByteArray(String filename) throws IOException, FileNotFoundException {

        File f = new File(filename);
        if (!f.exists()) {
            throw new FileNotFoundException(filename);
        }

        try (ByteArrayOutputStream bos = new ByteArrayOutputStream((int) f.length())) {
            BufferedInputStream in = null;
            in = new BufferedInputStream(new FileInputStream(f));
            int bufSize = 1024;
            byte[] buffer = new byte[bufSize];
            int len = 0;
            while (-1 != (len = in.read(buffer, 0, bufSize))) {
                bos.write(buffer, 0, len);
            }
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        }
    }
}

package com.stat;

import java.util.Date;

public class MyClassTest {
    public static void main(String[] args)
    {
        try {
            //注意這個路徑:是在target目錄下的,是編譯后的路徑
            Class classDate = new MyClassLoader("D:\\javaworkspace\\test02\\test02_module01\\target\\classes\\com\\stat").loadClass("com.stat.MyDate");
            Class classDate2 = new MyClassLoader("D:\\javaworkspace\\test02\\test02_module01\\target\\classes\\com\\stat").loadClass("MyDate");
            Date date = (Date) classDate.newInstance();
            System.out.println("date ClassLoader:"+date.getClass().getClassLoader().getClass().getName());
            System.out.println(date);

            Date date2 = (Date) classDate2.newInstance();
            System.out.println("date2 ClassLoader:"+date2.getClass().getClassLoader().getClass().getName());
            System.out.println(date2);
        } catch (Exception e1) {
            e1.printStackTrace();
        }

    }

}

輸出結果如下

classFile path==D:\javaworkspace\test02\test02_module01\target\classes\com\stat/MyDate.class
date ClassLoader:sun.misc.Launcher$AppClassLoader
MyDate : 我的時間。
date2 ClassLoader:com.stat.MyClassLoader
MyDate : 我的時間。

可以看到date 的類加載器是AppClassLoader,而date2的類加載器是自定義的

大家可以看到classdate和classDate2 這2個類,我們在用classLoader去加載的時候傳的參數唯一的不同就是前者傳入了完整的包名,而后者沒有。這就導致了前者的classLoader依舊是系統自帶的appclassloader 而后者才是我們自定義的classloader。 原因:

雖然對於classDate和classDate2來說,我們手動指定了她的類加載是我們自定義的myclassloader,但是根據類加載器的規則,我們能用父親的loadclass就肯定不會用自己的,而我們系統類加載器,AppClassLoader要想loadclass成功是需要傳入完整的包名的。所以classDate的構造還是傳入了完整的包名,這就是為啥classDate的加載器還是AppClassLoader,但是classDate2並沒有傳入完整的包名,所以AppClassLoader也是找不到這個CustomDate類的,最后只能交給MyClassLoader這個最底層的,我們自定義的classloader來load


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM