1. 程式人生 > >ClassLoader類加載器

ClassLoader類加載器

類加載器

先看例子:

public class ClassLoaderTest{
    public static void main(String[] args) {
        ClassLoader cl = ClassLoaderTest.class.getClassLoader();
        System.out.println(cl);
        System.out.println(cl.getParent());
        System.out.println(cl.getParent().getParent());
        /**
         * List是rt.jar包下
         */
        System.out.println(List.class.getClassLoader());
    }
}

輸出結果:

[email protected]
[email protected]
null
null


系統默認三個類加載器

BootStrap(根加載器):java核心庫(rt.jar),C/C++寫,故而打印出 null

ExtClassLoader(擴展加載器):ext包下

AppClassLoader:classpath下


類加載器的委托機制:

當Java虛擬機要加載第一個類的時候,到底派出哪個類加載器去加載呢?

(1). 首先當前線程的類加載器去加載線程中的第一個類(當前線程的類加載器:Thread類中有一個get/setContextClassLoader(ClassLoader cl);方法,可以獲取/指定本線程中的類加載器)

(2). 如果類A中引用了類B,Java虛擬機將使用加載類A的類加載器來加載類B

(3). 還可以直接調用ClassLoader.loadClass(String className)方法來指定某個類加載器去加載某個類

每個類加載器加載類時,又先委托給其上級類加載器當所有祖宗類加載器沒有加載到類,回到發起者類加載器,還加載不了,則會拋出ClassNotFoundException,不是再去找發起者類加載器的兒子,因為沒有getChild()方法。例如:如上圖所示: MyClassLoader->AppClassLoader->ExtClassLoader->BootStrap.自定定義的MyClassLoader1首先會先委托給AppClassLoader,AppClassLoader會委托給ExtClassLoader,ExtClassLoader會委托給BootStrap,這時候BootStrap就去加載,如果加載成功,就結束了。如果加載失敗,就交給ExtClassLoader去加載,如果ExtClassLoader加載成功了,就結束了,如果加載失敗就交給AppClassLoader加載,如果加載成功,就結束了,如果加載失敗,就交給自定義的MyClassLoader1類加載器加載,如果加載失敗,就報ClassNotFoundException異常,結束。


在看一個例子 :

public class MyClassLoader extends ClassLoader{
    protected MyClassLoader(ClassLoader parent) {
        super(parent);
    }
    protected MyClassLoader() {
        super();
    }
}

public class ClassLoaderTest2 {
    public static void main(String[] args) {
        MyClassLoader ml1 = new MyClassLoader();
        System.out.println(ml1.getParent());
        MyClassLoader ml2 = new MyClassLoader(Thread.currentThread().getContextClassLoader().getParent());
        System.out.println(ml2.getParent());
    }
}

打印結果:

[email protected]
[email protected]

說明自定義類加載器的父加載器默認是 :AppClassLoader


自定義的類加載器必須繼承抽象類ClassLoader

一般只需 重寫findClass方法,其實他內部還有一個loadClass方法和defineClass方法;

看loadClass源碼:

public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    //同步處理
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        //檢查該類是否已經加載
        Class c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                //如果有parent,就讓parent加載,如果找不到該類,
                //拋ClassNotFoundException ,讓子加載器加載,直到加載成功結束
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    //如果自定義加載器parent為null,直接用根加載器加載
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                //自定義加載器重寫該方法
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            解析class文件
            resolveClass(c);
        }
        return c;
    }
}

//該方法直接拋出異常,就是為了重寫,所以自定義加載器只需要重寫findClass,如果重寫
// load方法,還需要重新寫容器的委托邏輯, 沒有必要
protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

defineClass這個方法很簡單就是將class文件的字節數組編程一個class對象,這個方法肯定不能重寫,內部實現是在C/C++代碼中實現的


自定義類加載器:

public class MyClassLoader extends ClassLoader{
    private String base_dir;

    protected MyClassLoader(ClassLoader parent) {
        super(parent);
    }
    protected MyClassLoader() {
        super();
    }
    public MyClassLoader(ClassLoader parent,String base_dir) {
            super(parent);
            this.base_dir = base_dir;
        }

    public MyClassLoader(String base_dir) {
        super();
        this.base_dir = base_dir;
    }

    //重寫findClass
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String path = this.base_dir+"\\"+name+".class";
        byte[] b = new byte[0];
        try {
            b = toByteArray(path);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(b.length == 0){
            return null;
        }
        //定義class信息,把class文件字節碼信息組裝成jvm的class信息
        return defineClass(null,b,0,b.length);
    }

    private byte[] toByteArray(String filename) throws IOException {

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

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

}

public class ClassLoaderTest3 {
    public static void main(String[] args) {
        MyClassLoader ml = new MyClassLoader("E:\\workspace\\mytest\\temp");
        try {
            Class helloClass = ml.loadClass("Hello");
            Object hello =  helloClass.newInstance();
            System.out.println(hello.getClass().getClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
}

打印結果:

[email protected]

這裏要聲明:很容易打印出 AppClassLoader,這是由於你本地idea創建的Hello.class文件可能存在 項目class目錄下,刪除,從其他目錄引入 即可;

現在來測試下自定義的類加載器:

public class Hello {
    public String sayHello(){
        return "hello classLoader";
    }
}

public class ClassLoaderTest3 {
    public static void main(String[] args) {
        //指定根路徑,可以根據實際情況引入
        MyClassLoader ml = new MyClassLoader(ClassLoader.getSystemClassLoader().getParent(),"E:\\workspace\\mytest\\temp");
        try {
            //加載
            Class helloClass = ml.loadClass("Hello");
            //初始化
            Object hello =  helloClass.newInstance();
            //調用  加載類的方法
            Object info = helloClass.getMethod("sayHello").invoke(hello);
            System.out.println(info);
            System.out.println(hello.getClass().getClassLoader());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

打印結果:

hello classLoader
[email protected]






本文出自 “11898338” 博客,謝絕轉載!

ClassLoader類加載器