自定義類加載器

The default implementation of this method searches for classes in the following order:

  1. Invoke findLoadedClass(String) to check if the class has already been loaded.
  2. Invoke the loadClass method on the parent class loader. If the parent is null the class loader built-in to the virtual machine is used, instead.
  3. Invoke the findClass(String) method to find the class.

稍微理解下:

  1. 首先調(diào)用findLoadedClass(String),如果被加載過直接返回true,沒有執(zhí)行下一個步驟
  2. 調(diào)用父級類加載器的loaderClass方法,如果父級類加載器為空,就使用JVM默認的類加載器
  3. 調(diào)用findClass方法

這個是模版方法設(shè)計模式的運用。

編寫自定義類加載器

需要繼承ClassLoader類,重寫findClass方法。

package lorenzo;
public class MyClassLoader extends ClassLoader {

    private String rootDir;

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

    private byte[] getClassData(String className) {
        String path = classNameToPath(className);
        try {
            InputStream ins = new FileInputStream(path);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int bufferSize = 4096;
            byte[] buffer = new byte[bufferSize];
            int bytesNumRead = 0;
            while ((bytesNumRead = ins.read(buffer)) != -1) {
                baos.write(buffer, 0, bytesNumRead);
            }

            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    private String classNameToPath(String className) {
        return rootDir + File.separatorChar
                + className.replace('.', File.separatorChar) + ".class";
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        System.out.println("自定義類加載器");
        byte[] classData = getClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            return defineClass(name, classData, 0, classData.length);
        }
    }
}
編寫需要加載的類
package lorenzo;
public class MyClass {
    @Override
    public String toString() {
        return "Hello Lorenzo!";
    }
}
編寫測試類
package lorenzo;
public class Client {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        MyClassLoader myClassLoader = new MyClassLoader("/data");
        Class clazz = myClassLoader.loadClass("lorenzo.MyClass");
        System.out.println(clazz.newInstance().toString());
        System.out.println(myClassLoader.getClass().getClassLoader());
    }
}

注意:以上三個類都在/data/lorenzo文件夾下面
mac控制臺輸入:

cd /data/lorenzo
javac MyClassLoader.java
javac MyClass.java
javac Client.java
cd ..
java ava lorenzo/Client

output:

Hello Lorenzo!
sun.misc.Launcher$AppClassLoader@2a139a55

分析結(jié)果:
根據(jù)類加載器樹形圖可知

類加載器樹.png

MyClassLoader.java和Client.java以及MyClass.java都在classpath目錄下,他們由AppClassLoader類加載器加載。根據(jù)loadClass(String)的模版方法可知,MyClassLoader的父級類加載器AppClassLoader可以加載binary name 為lorenzo.MyClass的類,程序不會執(zhí)行到loadClass的第三步,所以是AppClassLoader而不是MyClassLoader加載MyClass。

修改代碼

把MyClass.class移動到data1/lorenzo的文件夾下
修改Client中代碼:

  MyClassLoader myClassLoader = new MyClassLoader("/data");

修改后的代碼為

MyClassLoader myClassLoader = new MyClassLoader("/data1");

mac控制臺輸入:

cd /data/lorenzo
javac Client.java
cd ..
java lorenzo/Client

output:

自定義類加載器
Hello Lorenzo!
sun.misc.Launcher$AppClassLoader@2a139a55

注意確保/data/lorenzo目錄沒有MyClass.class。

把自定義的類加載器掛載到ExtClassLoader
  1. 把MyClassLoader.class打成jar包

cd /data
jar cvf lorenzo.jar lorenzo/MyClassLoader.class

  1. 把lorenzo.jar復(fù)制到../jre/lib/ext/目錄下

java lorenzo/Client

output:

自定義類加載器
Hello Lorenzo!
sun.misc.Launcher$ExtClassLoader@75b84c92

寫在最后

加密的類加載器思路,首先編譯生成class文件,加密class文件,在自定義的類加載器解密。

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

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

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