如何理解和運(yùn)用ClassLoader
定義
根據(jù)《深入理解Java虛擬機(jī)》提到“通過(guò)一個(gè)類的全限定名(packageName.ClassName)來(lái)獲取描述此類的二進(jìn)制字節(jié)(class文件字節(jié))這個(gè)動(dòng)作的代碼模塊就叫做類加載器(ClassLoader)”。
作用
1、通常類加載器的作用是加載資源(字節(jié)碼文件)到j(luò)ava虛擬機(jī)中,想要在一個(gè)jvm 進(jìn)程中唯一確認(rèn)一個(gè)類,除了類的全限定名外,還需要指定它是由哪個(gè)類加載器加載的。
2、比如我們的類庫(kù)需要通過(guò)遠(yuǎn)程網(wǎng)絡(luò)獲取,可以通過(guò)自定義類加載器從遠(yuǎn)程加載字節(jié)碼文件。
3、java的字節(jié)碼文件很容易反編譯出來(lái),一些核心的代碼不想被反編譯出來(lái),可以對(duì)字節(jié)碼進(jìn)行加密,然后通過(guò)自定義的類加載器加載這些字節(jié)碼,然后進(jìn)行解碼返回給虛擬機(jī)。
4、比如jvm的熱加載和熱部署等功能也需要自定義類加載器來(lái)完成。
.......
java類加載器的種類
1、Bootstrap ClassLoader : 該加載器是最頂層的類加載器,它是加載放在{Java_home}\lib目錄 或者-Xbootclasspath指定路徑下類庫(kù)。
2、Extension ClassLoader : 該類加載器負(fù)載加載{Java_home}/lib\ext目錄 或者System.getenv("java.ext.dirs")系統(tǒng)變量路徑下的類庫(kù)。
3、Application ClassLoader : 該類加載器加載用戶程序類路徑下的類庫(kù),它是默認(rèn)的程序的類加載器。
雙親委派機(jī)制
1、雙親委派機(jī)制,雙親委派除了啟動(dòng)類加載器(Bootstrap ClassLoader)外,其他的類加載器都應(yīng)該有自己父加載器。它們的實(shí)現(xiàn)不是通過(guò)繼承來(lái)實(shí)現(xiàn)的,而是通過(guò)組合的方式。當(dāng)加載某個(gè)Class時(shí),當(dāng)前類加載器會(huì)把這個(gè)加載請(qǐng)求委派給其父類加載器加載,同理父類加載器同樣委派其它的父類加載器加載,直到無(wú)其父類類加載器加載為止,如果父類加載器加載失敗,才會(huì)由其子類加載。
2、使用雙親委派機(jī)制,有個(gè)明顯的特征是:Java類隨著它的類加載器一起具備了一種優(yōu)先級(jí)的層次關(guān)系。比如rt.jar包中的java.lang.Object,由于它所在位置是由啟動(dòng)類加載器加載,所以O(shè)bject類在程序的各種類加載器環(huán)境中都是同一個(gè)類。
3、雙親委派模型如下:

ClassLoader
可以看下ClassLoader 雙親委派模型的大致代碼框架如下:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 1、查看該類是否加載
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
// 2、如果未加載,委托給父類加載器加載
if (parent != null) {
c = parent.loadClass(name, false);
} else {
//3、沒有父類加載器,委托給BootstrapClassLoader
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// 父類加載器沒有加載到,則自己加載
long t1 = System.nanoTime();
c = findClass(name);
// 記錄該類加載的狀態(tài)Stat.
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
// resolve :true,需要對(duì)類進(jìn)行鏈接(鏈接階段包括:準(zhǔn)備,解析,初始化類)
if (resolve) {
resolveClass(c);
}
return c;
}
}
1、通過(guò)以上可以知道,我們可以繼承ClassLoader 來(lái)實(shí)現(xiàn)自己的類加載器,然后重寫findClass()方法,這些還是保存了雙親委派機(jī)制。
2、當(dāng)我們重寫findClass()方法時(shí),得到該類的字節(jié)碼后,需要調(diào)用defineClass()來(lái)放回Class<?>對(duì)象。
URLClassLoader
1、一般自定義的類加載器可以直接繼承該類,該類加載器通過(guò)添加url路徑來(lái)獲取類。
2、可以在其構(gòu)造函數(shù)上URLClassLoader(URL[] urls, ClassLoader parent)直接進(jìn)行添加其URL。
3、也可以通過(guò)addURL(URL)添加其URL。
自定義類加載器
1、首先假設(shè)main-project為我們自己編寫的工程,其依賴某一api:service-spi。而service-spi的實(shí)現(xiàn)有很多,project-spi-impl是其中的一個(gè)。
2、當(dāng)main-project僅依賴service-spi,而project-spi-impl不在工程的類加載路徑下。所以需要自定義類加載器,從某個(gè)路徑下的jar加載進(jìn)來(lái)。CoreClassLoader就是自定義的類加載器。
3、CoreClassLoader繼承自URLClassLoader,然后把相關(guān)搜索路徑添加到該類加載器即可。
4.github:https://github.com/zhvqee/class-loader
SpringBoot 對(duì)類加載器的運(yùn)用
1、SpringBoot 工程通過(guò)spring-boot-maven-plugin插件打包。把相關(guān)資源和依賴包都打到一個(gè)jar包中(all in one)。其包的結(jié)構(gòu)如下:

BOOT-INF/classes:存放的是本工程的class文件
BOOT-INF/lib:存放的是本工程依賴的二方包和三方包。
META-INF/MANIFEST.MF:該文件記錄了程序啟動(dòng)入口等。
o.s.b.loader包下就是springboot自定義的類加載器。
2、當(dāng)我們執(zhí)行java -jar xxxx 時(shí),會(huì)讀取MANIFEST.MF下
Main-Class: org.springframework.boot.loader.JarLauncher,
該配置就是程序的路口。而我們編寫的Main方法不是真正的啟動(dòng)的入口。
3、當(dāng)執(zhí)行Launcher#launch()方法時(shí),會(huì)把SpringBoot自定義的類加載器LaunchedURLClassLoader設(shè)置線程的上下文中,并通過(guò)該自定義類加載器加載我們自己編寫的main方法所在的類,然后利用反射調(diào)用main方法。代碼片段如下:
Class<?> mainClass = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName);
Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
mainMethod.invoke(null, new Object[] { this.args });
4、LaunchedURLClassLoader 類加載器繼承URLClassLoader類加載器,它加載的路徑就是可執(zhí)行jar下BOOT-INF下的class文件和二方包、三方包。
Class.forName() 和 ClassLoader.load()
1、JVM 加載的類主要經(jīng)過(guò)以下幾個(gè)步驟:加載,鏈接,初始化,試用,卸載。
2、Class.forName()默認(rèn)是需要對(duì)加載的類進(jìn)行初始化。
3、ClassLoader.load實(shí)際調(diào)用的是ClassLoader.load(className,false),false:表示不進(jìn)行鏈接,不進(jìn)行鏈接也就代表不會(huì)進(jìn)行初始化的操作,類的靜態(tài)塊和靜態(tài)對(duì)象都不會(huì)執(zhí)行。
以上就是如何理解和運(yùn)用ClassLoader的詳細(xì)內(nèi)容,更多關(guān)于理解和運(yùn)用 ClassLoader的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- jvm之java類加載機(jī)制和類加載器(ClassLoader)的用法
- Java類加載器ClassLoader用法解析
- ClassLoader類加載源碼解析
- Java運(yùn)行時(shí)環(huán)境之ClassLoader類加載機(jī)制詳解
- 深入Spring Boot之ClassLoader的繼承關(guān)系和影響
- 關(guān)于Android中自定義ClassLoader耗時(shí)問(wèn)題的追查
- 淺談Android Classloader動(dòng)態(tài)加載分析
- Java Classloader機(jī)制用法代碼解析
- Java中ClassLoader類加載學(xué)習(xí)總結(jié)
- 詳解Android類加載ClassLoader
相關(guān)文章
老生常談spring boot 1.5.4 日志管理(必看篇)
下面小編就為大家?guī)?lái)一篇老生常談spring boot 1.5.4 日志管理(必看篇)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06
Spring中property-placeholder的使用與解析詳解
本篇文章主要介紹了Spring中property-placeholder的使用與解析詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05
SpringBoot中@EnableAsync和@Async注解的使用小結(jié)
在SpringBoot中,可以通過(guò)@EnableAsync注解來(lái)啟動(dòng)異步方法調(diào)用的支持,通過(guò)@Async注解來(lái)標(biāo)識(shí)異步方法,讓方法能夠在異步線程中執(zhí)行,本文就來(lái)介紹一下,感興趣的可以了解一下2023-11-11
springBoot使用openfeign來(lái)遠(yuǎn)程調(diào)用的實(shí)現(xiàn)
這篇文章主要介紹了springBoot使用openfeign來(lái)遠(yuǎn)程調(diào)用的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Java ArrayList擴(kuò)容問(wèn)題實(shí)例詳解
這篇文章主要介紹了Java ArrayList擴(kuò)容問(wèn)題實(shí)例詳解,分享了相關(guān)代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02
Java實(shí)現(xiàn)根據(jù)地址智能識(shí)別省市區(qū)縣
這篇文章主要為大家詳細(xì)介紹了如何編寫一個(gè)Java工具類,可以根據(jù)身份證地址或用戶輸入的地址,智能識(shí)別并提取出詳細(xì)的省市區(qū)縣信息,感興趣的小伙伴可以了解下2025-03-03
序列化版本號(hào)serialVersionUID的作用_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
Java序列化是將一個(gè)對(duì)象編碼成一個(gè)字節(jié)流,反序列化將字節(jié)流編碼轉(zhuǎn)換成一個(gè)對(duì)象,這篇文章主要介紹了序列化版本號(hào)serialVersionUID的作用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05

