Java中的ClassLoader雙親委派機(jī)制詳解
Java ClassLoader雙親委派機(jī)制
什么是雙親委派模型
“類(lèi)加載體系”及ClassLoader雙親委派機(jī)制。java程序中的 .java文件編譯完會(huì)生成 .class文件,而 .class文件就是通過(guò)被稱(chēng)為類(lèi)加載器的ClassLoader加載的,而ClassLoder在加載過(guò)程中會(huì)使用“雙親委派機(jī)制”來(lái)加載 .class文件。

- BootStrapClassLoader:?jiǎn)?dòng)類(lèi)加載器,該ClassLoader是jvm在啟動(dòng)時(shí)創(chuàng)建的,用于加載 $JAVA_HOME/jre/lib下面的類(lèi)庫(kù)(或者通過(guò)參數(shù)-Xbootclasspath指定)。由于引導(dǎo)類(lèi)加載器涉及到虛擬機(jī)本地實(shí)現(xiàn)細(xì)節(jié),開(kāi)發(fā)者無(wú)法直接獲取到啟動(dòng)類(lèi)加載器的引用,所以不能直接通過(guò)引用進(jìn)行操作(由c++編寫(xiě))。
- ExtClassLoader:擴(kuò)展類(lèi)加載器,該ClassLoader是在sun.misc.Launcher里作為一個(gè)內(nèi)部類(lèi)ExtClassLoader定義的(即 sun.misc.Launcher$ExtClassLoader),ExtClassLoader會(huì)加載 $JAVA_HOME/jre/lib/ext下的類(lèi)庫(kù)(或者通過(guò)參數(shù)-Djava.ext.dirs指定)。
- AppClassLoader:應(yīng)用程序類(lèi)加載器,該ClassLoader同樣是在sun.misc.Launcher里作為一個(gè)內(nèi)部類(lèi)AppClassLoader定義的(即 sun.misc.Launcher$AppClassLoader),AppClassLoader會(huì)加載java環(huán)境變量CLASSPATH所指定的路徑下的類(lèi)庫(kù),而CLASSPATH所指定的路徑可以通過(guò)System.getProperty(“java.class.path”)獲??;當(dāng)然,該變量也可以覆蓋,可以使用參數(shù)-cp,例如:java -cp 路徑 (可以指定要執(zhí)行的class目錄)。
- CustomClassLoader:自定義類(lèi)加載器,該ClassLoader是指我們自定義的ClassLoader,比如tomcat的StandardClassLoader屬于這一類(lèi);當(dāng)然,大部分情況下使用AppClassLoader就足夠了。
ClassLoader的loadClass(String name, boolean resolve)源碼:
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}代碼很明朗:
首先找緩存(findLoadedClass),沒(méi)有的話(huà)就判斷有沒(méi)有parent,有的話(huà)就用parent來(lái)遞歸的loadClass,然而ExtClassLoader并沒(méi)有設(shè)置parent,則會(huì)通過(guò)findBootstrapClassOrNull來(lái)加載class,而findBootstrapClassOrNull則會(huì)通過(guò)JNI方法
private native Class findBootstrapClass(String name)
來(lái)使用BootStrapClassLoader來(lái)加載class。
然后如果parent未找到class,則會(huì)調(diào)用findClass來(lái)加載class,findClass是一個(gè)protected的空方法,可以覆蓋它以便自定義class加載過(guò)程。
另外,雖然ClassLoader加載類(lèi)是使用loadClass方法,但是鼓勵(lì)用 ClassLoader 的子類(lèi)重寫(xiě) findClass(String),而不是重寫(xiě)loadClass,這樣就不會(huì)覆蓋了類(lèi)加載默認(rèn)的雙親委派機(jī)制。
雙親委派機(jī)制為什么安全
舉個(gè)例子,ClassLoader加載的class文件來(lái)源很多,比如編譯器編譯生成的class、或者網(wǎng)絡(luò)下載的字節(jié)碼。而一些來(lái)源的class文件是不可靠的,比如我可以自定義一個(gè)java.lang.Integer類(lèi)來(lái)覆蓋jdk中默認(rèn)的Integer類(lèi),
例如下面這樣:
package java.lang;
public class Integer {
public Integer(int value) {
System.exit(0);
}
}初始化這個(gè)Integer的構(gòu)造器是會(huì)退出JVM,破壞應(yīng)用程序的正常進(jìn)行,如果使用雙親委派機(jī)制的話(huà)該Integer類(lèi)永遠(yuǎn)不會(huì)被調(diào)用,因?yàn)槲蠦ootStrapClassLoader加載后會(huì)加載JDK中的Integer類(lèi)而不會(huì)加載自定義的這個(gè),可以看下下面這測(cè)試個(gè)用例:
public static void main(String... args) {
Integer i = new Integer(1);
System.err.println(i);
}執(zhí)行時(shí)JVM并未在new Integer(1)時(shí)退出,說(shuō)明未使用自定義的Integer,于是就保證了安全性。
為什么需要雙親委派模型
為什么需要雙親委派模型呢?假設(shè)沒(méi)有雙親委派模型,試想一個(gè)場(chǎng)景:
黑客自定義一個(gè)java.lang.String類(lèi),該String類(lèi)具有系統(tǒng)的String類(lèi)一樣的功能,只是在某個(gè)函數(shù)稍作修改。比如equals函數(shù),這個(gè)函數(shù)經(jīng)常使用,如果在這這個(gè)函數(shù)中,黑客加入一些“病毒代碼”。并且通過(guò)自定義類(lèi)加載器加入到JVM中。此時(shí),如果沒(méi)有雙親委派模型,那么JVM就可能誤以為黑客自定義的java.lang.String類(lèi)是系統(tǒng)的String類(lèi),導(dǎo)致“病毒代碼”被執(zhí)行。
而有了雙親委派模型,黑客自定義的java.lang.String類(lèi)永遠(yuǎn)都不會(huì)被加載進(jìn)內(nèi)存。因?yàn)槭紫仁亲铐敹说念?lèi)加載器加載系統(tǒng)的java.lang.String類(lèi),最終自定義的類(lèi)加載器無(wú)法加載java.lang.String類(lèi)。
或許你會(huì)想,我在自定義的類(lèi)加載器里面強(qiáng)制加載自定義的java.lang.String類(lèi),不去通過(guò)調(diào)用父加載器不就好了嗎?確實(shí),這樣是可行。但是,在JVM中,判斷一個(gè)對(duì)象是否是某個(gè)類(lèi)型時(shí),如果該對(duì)象的實(shí)際類(lèi)型與待比較的類(lèi)型的類(lèi)加載器不同,那么會(huì)返回false。
舉個(gè)簡(jiǎn)單例子:
- ClassLoader1、ClassLoader2都加載java.lang.String類(lèi),對(duì)應(yīng)Class1、Class2對(duì)象。
- 那么Class1對(duì)象不屬于ClassLoad2對(duì)象加載的java.lang.String類(lèi)型。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
淺談java中對(duì)集合對(duì)象list的幾種循環(huán)訪問(wèn)
下面小編就為大家?guī)?lái)一篇java中對(duì)集合對(duì)象list的幾種循環(huán)訪問(wèn)詳解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-07-07
詳解RabbitMQ延遲隊(duì)列的基本使用和優(yōu)化
這篇文章主要介紹了詳解RabbitMQ延遲隊(duì)列的基本使用和優(yōu)化,延遲隊(duì)列中的元素都是帶有時(shí)間屬性的。延遲隊(duì)列就是用來(lái)存放需要在指定時(shí)間被處理的元素的隊(duì)列,需要的朋友可以參考下2023-05-05
Intellij Idea新建SpringBoot項(xiàng)目方式
這篇文章主要介紹了Intellij Idea新建SpringBoot項(xiàng)目方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-09-09
IntelliJ IDEA快速創(chuàng)建getter和setter方法
這篇文章主要介紹了IntelliJ IDEA快速創(chuàng)建getter和setter方法,本文通過(guò)圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03

