3.程式碼實現自定義類載入器

手把手叫你寫類載入器。

了解了類載入器的雙親委派機制, 也知道了雙親委派機制的原理,接下來就是檢驗我們學習是否紮實了,來自定義一個類載入器

一. 回顧類載入器的原理

還是這張圖,類載入器的入口是c++調用java程式碼創建了JVM啟動器,其中的一個啟動器是sun.misc.Launcher啟動器。這個啟動器啟動並載入的AppClassLoader和ExtClassLoader。然後調用launcher.getClassLoader()方法獲取loader對象, loader對象本質是一個ClassLoader,然後調用了ClassLoader的loadClass(“…”)方法載入類。也是在loadClass(“…”)方法里實現了雙親委派機制。

詳細原理參考文章://www.cnblogs.com/ITPower/p/15363400.html

二、自定義類載入器分析

對於類載入器, 我們知道他的重點是loadClass(…)方法, 裡面的雙親委派機制也是在loadClass方法裡面實現的. loadClass方法裡面實際上去載入類的是findClass()方法. 對於我們自定義的類載入器來說需要做到兩點即可

  1. 這個自定義的類載入器繼承自ClassLoader

  2. 這個類載入器要重寫ClassLoader類中的findClass()方法

另外我們還可以參考AppClassLoader和ExtClassLoader來寫。

三、自定義類載入器實現

下面我自己定義了一個類載入器

第一步:自定義類載入器繼承自ClassLoader抽象類,然後定義一個構造方法, 用來接收要載入的類名

第二步:重寫核心方法findClass(String name)

這裡有兩步操作,

第一個是: 從類路徑中讀取要載入類的文件內容, 自定義

第二個是: 調用構造類的方法, 調用的系統的defineClass

接下來看看自定義的loadByte是如何實現的

這裡的實現就是找到類, 並且將類的內容讀取出來, 轉換成二進位的位元組碼, 返回

最後一部分就是如何調用了.

用類載入器載入類, 然後實例化, 使用反射機制調用User1 的方法sout

package com.lxl.jvm;

public class User1 {
    public void sout() {
        System.out.println("進入到User1");
    }
}

這裡面System.out.println(clazz.getClassLoader().getClass().getName()); 獲取當前類的類載入器, 猜一猜這裡列印的會是誰?

看到了么? 是AppClassLoader, 為什麼呢?

原因是我的項目里已經有一個類User1了

我們自定義類載入器的父類是AppClassLoader. 而程式程式碼中的User1剛好是被AppClassLoader載入, 因為找到了,所以就不會再去我們指定的文件夾中查找了

這就是類的雙親委派機制的特點.

那麼如果我們將項目中的User1類刪除掉, 這是類載入器是誰呢? 當然就是我們自定義的類載入器了.

那麼問題來了, 自定義類載入器的父類為什麼是AppClassLoader呢?

四. 分析自定義類載入的父類為什麼是appClassLoader?

我們來看一下源碼

我們自定義的類載入器, 繼承自ClassLoader類載入器, 那麼在調用自定義類載入器的構造方法之前, 應該先載入父類ClassLoader的無參構造函數.

首先會執行ClassLoader的無參的構造方法.

而無參的構造方法會調用自身的構造方法

裡面有一個parent, 我們就是要看看這個parent到底是誰呢. 來看看getSystemClassLoader()方法


之前我們已經研究過getClassLoader()這個方法了, 這裡面定義的loadClass是誰呢?就是AppClassLoader.


這就是為什麼自定義class類載入器的父類是AppClassLoader的原因了。