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()方法. 對於我們自定義的類載入器來說需要做到兩點即可
-
這個自定義的類載入器繼承自ClassLoader
-
這個類載入器要重寫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的原因了。