JVM 自定義的類加載器的實現和使用


1、用戶自定義的類加載器:

要創建用戶自己的類加載器,只需要擴展java.lang.ClassLoader類,然后覆蓋它的findClass(String name)方法即可,該方法根據參數指定類的名字,返回對應的Class對象的引用。

findClass
protected Class<?> findClass(String name)
                      throws ClassNotFoundException
使用指定的二進制名稱查找類。此方法應該被類加載器的實現重寫,該實現按照委托模型來加載類。在通過父類加載器檢查所請求的類后,此方法將被 loadClass 方法調用。默認實現拋出一個 ClassNotFoundException。
參數:
name - 類的二進制名稱
返回:
得到的 Class 對象
拋出:
ClassNotFoundException - 如果無法找到類
從以下版本開始:
1.2


創建用戶自定義的類加載器:

public class MyClassLoader extends ClassLoader {

    //類加載器名稱
    private String name;
    //加載類的路徑
    private String path = "D:/";
    private final String fileType = ".class";
    public MyClassLoader(String name){
        //讓系統類加載器成為該 類加載器的父加載器
        super();
        this.name = name;
    }

    public MyClassLoader(ClassLoader parent, String name){
        //顯示指定該類加載器的父加載器
        super(parent);
        this.name = name;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    @Override
    public String toString() {
        return this.name;
    }

    /**
     * 獲取.class文件的字節數組
     * @param name
     * @return
     */
    private byte[] loaderClassData(String name){
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        this.name = this.name.replace(".", "/");
        try {
            is = new FileInputStream(new File(path + name + fileType));
            int c = 0;
            while(-1 != (c = is.read())){
                baos.write(c);
            }
            data = baos.toByteArray();

        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            try {
                is.close();
                baos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return data;
    }

    /**
     * 獲取Class對象
     */
    @Override
    public Class<?> findClass(String name){
        byte[] data = loaderClassData(name);
        return this.defineClass(name, data, 0, data.length);
    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        //loader1的父加載器為系統類加載器
        MyClassLoader loader1 = new MyClassLoader("loader1");
        loader1.setPath("D:/lib1/");
        //loader2的父加載器為loader1
        MyClassLoader loader2 = new MyClassLoader(loader1, "loader2");
        loader2.setPath("D:/lib2/");
        //loader3的父加載器為根類加載器
        MyClassLoader loader3 = new MyClassLoader(null, "loader3");
        loader3.setPath("D:/lib3/");

        Class clazz = loader2.loadClass("Sample");
        Object object = clazz.newInstance();
    }
}
public class Sample {

    public Sample(){
        System.out.println("Sample is loaded by " + this.getClass().getClassLoader());
        new A();
    }
}
public class A {

    public A(){
        System.out.println("A is loaded by " + this.getClass().getClassLoader());
    }
}

當執行loader2.loaderClass("Sample")時,先由它上層的所有父加載器嘗試加載Sample類。loader1從D:/lib1/目錄下成功的加載了Sample類,因此laoder1是Sample類的定義類加載器,loader1和loader2是Sample類的初始類加載器。

當執行loader3.loadClass("Sample")時,先由它上層的所有父加載器嘗試加載Sample類。loader3的父加載器為根類加載器,它無法加載Sample類,接着loader3從D:/lib3/目錄下成功地加載了Sample類,因此loader3是Sample類的定義類加載器即初始類加載器。

在Sample類中主動使用了A類,當執行Sample類的構造方法中的new A()語句時,Java虛擬機需要先加載Dog類,Java虛擬機會勇Sample類的定義類加載器去加載Dog類,加載過程也同樣采用父親委托機制。

2、不同類加載器的命名空間關系:

同一個命名空間內的類是相互可見的。

子加載器的命名空間包含所有父加載器的命名空間。因此子加載器加載的類能看見父加載器加載的類。例如系統類加載器加載的類能看見根類加載器加載的類。

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

如果兩個加載器之間沒有直接或間接的父子關系,那么它們各自加載的類相互不可見。

當兩個不同命名空間內的類相互不可見時,可以采用Java的反射機制來訪問實例的屬性和方法。

轉載自:http://www.itzhai.com/java-virtual-machine-notes-custom-class-loader-implementation-and-use-of.html


免責聲明!

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



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