天天看點

Classloader 二 自定義類加載器一 為什麼需要自定義類加載器二 示例三 總結

一 為什麼需要自定義類加載器

1.假設需要加載的類需要保密,那麼java自帶的app類加載器就無法完成加載的任務,這個時候就需要通過自定義類加載器先對類檔案進行解密,然後再進行加載。

2.加載指定路徑的類檔案。比如類檔案放在磁盤的某個檔案夾、或者來自網絡。下面的例子會展示加載指定磁盤目錄下的一個類檔案的方法。

二 示例

自定義一個類加載器MyClassLoader,繼承自ClassLoader,重寫findClass方法(前一篇講到了為什麼重寫findClass而不是loadClass,主要是為了遵循雙親委托模型)。代碼如下:

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

public class MyClassLoader extends ClassLoader {

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // TODO Auto-generated method stub
        try {
            File location = new File("D:\\bkp");
            byte[] b = loadClassData(name, location);
            return defineClass(name, b, , b.length);
        } catch (Exception e) {
            throw new ClassNotFoundException(name);
        }
    }



    @Override

    public Class<?> loadClass(String arg0) throws ClassNotFoundException {
        // TODO Auto-generated method stub
        return super.loadClass(arg0);
    }



    protected byte[] loadClassData(String name, File location) {
        FileInputStream fis = null;
        byte[] datas = null;
        try {
            File classFile = new File(location, name + ".class");
            fis = new FileInputStream(classFile);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int b;
            while ((b = fis.read()) != -) {
                bos.write(b);
            }
            datas = bos.toByteArray();
            bos.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fis != null)
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
        return datas;

    }

}

           

然後使用這個類加載器嘗試加載一些不同類型的類檔案,我們來看一下結果:

import java.lang.reflect.Method;


public class ClassLoaderTest {
        public static void main(String[] args) throws Exception {
            ClassLoader myLoader = new MyClassLoader();
            Class<?> clazz = myLoader.loadClass("ClassLoaderTest");
            Object obj = clazz.newInstance();
            System.out.println(obj.getClass().getClassLoader());
        }
}
           

第一次

傳入參數ClassLoaderTest

結果:[email protected]

第二次

傳入參數java.lang.String

結果:null

null代表使用的是bootstrap類加載器進行的加載。因為這次傳入參數是java.lang.String,根據雙親委派原則,最終會請求bootstrap類加載器加載java.lang.String類,而java.lang.String類是屬于bootstrap類加載器加載的内容範圍。

第三次

傳入參數Demo,這個類不在應用程式classpath目錄,而是放在d:\bkp目錄

結果:[email protected]。這次才是通過自定義類加載器完成了類加載。

三 總結

上例通過寫一個類加載器說明了自定義類加載器的基本流程,為了遵循雙親委托模型,我們在自定義類加載器中重寫了findClass方法。如果重寫loadClass同樣可以實作自定義類加載器,但是我們上例中得到的結果就會不同,因為破壞了雙親加載模型,最後得到的結果都是自定義類加載器。