標籤:public fine 網路 server 初始 cep nat 目錄 心得
本文是筆者在學習Java 類載入的過程中,整理的心得體會,共勉!
類載入的意義
簡單概念:
將Java類的.class檔案中的位元據讀入到記憶體中,放置在運行時資料區的方法區內。
什麼時候會進行類載入?
本地系統載入;
代理載入,比如Spring的AOP動態代理;
從jar包中載入;
等等等等
ClassLoader類結構介紹
ClassLoader是一個抽象類別,核心方法如下:
defineClass():目的是將byte位元組流解析成JVM能夠識別的Class對象。
findClass():此方法支援重載,與defineClass()配合使用,目的是擷取Class對象的位元組碼。這個方法的意義在於,我們不僅僅可以通過class檔案執行個體化對象,也可以通過其他方式,比如從網路上擷取的位元組碼檔案,可能會有對應的加密規則。
loadClass():此方法支援重載,目的是擷取載入類的類對象。
resolveClass():實現讓JVM連結這個類,此方法調用的是本地方法,不能重載。
Class類的生命週期
此生命週期對應到ClassLoader類的具體方法:
載入:loadClass()、findClass()、defineClass()
串連:包括驗證、準備、解析:resolveClass() 本地native方法
初始化:JVM負責
類載入機制(雙親委託)
概念介紹:
大致的ClassLoader分類:
- Bootstrap ClassLoader:負責載入存放在JDK\jre\lib(JDK代表JDK的安裝目錄,下同)下,或被 -Xbootclasspath參數指定的路徑中的,並且能被虛擬機器識別的類庫
- ExtClassLoader:負責載入JDK\jre\lib\ext目錄中,或者由 java.ext.dirs系統變數指定的路徑中的所有類庫(如javax.開頭的類),開發人員可以直接使用擴充類載入器
- AppClassLoader:負責載入使用者類路徑(ClassPath)所指定的類,其父類是ExtClassLoader
心得體會:
這裡的父載入器與子載入器 在java類別關係上並不是繼承關係;
實現自己的類載入器,不管是直接實現抽象類別ClassLoader,還是繼承其他子類,父載入器都是AppClassLoader;
類載入的方式
1、命令列啟動應用時候由JVM初始化載入
2、通過Class.forName()方法動態載入
3、通過ClassLoader.loadClass()方法動態載入
Class.forName()和ClassLoader.loadClass()區別
Class.forName():將類的.class檔案載入到jvm中之外,還會對類進行解釋,執行類中的static塊;
ClassLoader.loadClass():只幹一件事情,就是將.class檔案載入到jvm中,不會執行static中的內容,只有在newInstance才會去執行static塊。
Class.forName(name,initialize,loader)帶參函數也可控制是否載入static塊。並且只有調用了newInstance()方法採用調用建構函式,建立類的對象
應用實踐實現自訂的ClassLoader
基於上面對於類載入器的分析,自訂ClassLoader的情況如下:
- 需要在自訂路徑下尋找自訂的class檔案
- 對我們想要載入的類做特殊處理,比如通過網路傳輸的類的位元組碼可能進行了加密,擷取這種類的位元組碼需要在我們自訂的ClassLoader中實現
- 實作類別的熱部署
從上面的分析我們還可以得到,自訂ClassLoader的實現思路可以通過繼承ClassLoader類,重寫findClass()來實現,當然你也可以重寫loadClass(),但是預設的loadClass()是實作類別載入雙親委託模式的,除非你想要破壞這種雙親委託模式,不然就別重寫了。
下面是我在本機寫的測試自訂ClassLoader的Demo:
/** * 自訂路徑下的class檔案載入 * @Auther: [email protected] * @Date: 2018/5/31 19:34 * @Description: */public class PathClassLoader extends ClassLoader { private String classPath; public PathClassLoader(String classPath) { this.classPath = classPath; } private String packageName = "com.trey.classloader.Test"; protected Class<?> findClass(String name) throws ClassNotFoundException { if(packageName.startsWith(name)) { byte[] classData = getData(name); if(classData == null) { throw new ClassNotFoundException(); } else { System.out.println("當前類載入器:"+this.getClass().getClassLoader().toString()); return defineClass(name, classData, 0, classData.length); } } else { return super.loadClass(name); } } private byte[] getData(String name) { String path = classPath + File.separatorChar + name.replace(‘.‘,File.separatorChar) + ".class"; try { InputStream is = new FileInputStream(path); ByteArrayOutputStream stream = new ByteArrayOutputStream(); byte[] buffer = new byte[2048]; int num = 0; while ((num = is.read(buffer)) != -1) { stream.write(buffer, 0, num); } return stream.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; }}
實作類別的熱部署
這是給自己布置的對類載入學習的總結練習。下面說下自己準備實現的思路:
- 實現一個server服務,監聽具體目錄下的所有class檔案的更新情況
- 當出現class檔案更新時,使用自訂的classLoader進行載入
Java 類載入ClassLoader學習心得