[TOC]java
類加載器是面試的高頻題,類加載器的雙親委派模型更是重中之重,基本問到類加載器就會問到雙親委派模型,那麼他究竟是什麼,又有什麼好處呢git
咱們先來個模擬個這樣的情景:github
咱們打仗的時候,另一個陌生的隊伍讓你調兵支援,你是自做主張,仍是向上級彙報,讓上級處理,請求上級指示,按照指示行動呢?面試
這個場景跟雙親委派的作法有一點類似,你能夠考慮下那種作法比較好,又有什麼好處。spring
個人全部文章同步更新與Github--Java-Notes,想了解JVM,HashMap源碼分析,spring相關,劍指offer題解(Java版),能夠點個star。能夠看個人github主頁,天天都在更新喲。bootstrap
邀請您跟我一同完成 repotomcat
上一節咱們知道了類加載的過程,其中加載階段的第一步"經過一個類的全限定名來獲取描述此類的二進制字節流"這個動做放到虛擬機外部實現,以便讓應用程序本身決定如何去獲取所須要的類。這個過程的代碼模塊就是類加載器。app
<JAVA_HOME>\lib
目錄中的或者被-Xbootclasspath
參數所指定的路徑中的,而且是虛擬機識別的類庫加載到虛擬機內存(注意三個關鍵詞)<JAVA_HOME>\lib\ext
目錄中的,或者被java.ext.dirs
系統變量所指定的路徑中的全部庫若是劃分的粗一點,那麼只用兩種ide
java.lang.ClassLoader
上面咱們已經介紹了類加載器,大體能夠分爲四種:模塊化
他們是相互配合工做的,若是類加載之間的層次是下圖這種關係,那麼就是雙親委派模型
雙親委派模型並不是強制性的約束模型,他只是一個Java設計者推薦的實現方式
比如這樣的情景:
咱們打仗的時候,另一個陌生的隊伍讓你調兵支援,這個時候你不能擅做主張,要向上級請示,讓上級來進行協調工做,作決定。上級作不了決定,你再嘗試隨機應變,結合實際狀況作決定。
那麼你結合情景想一想,這樣作的好處是啥呢?
不會無組織無紀律。
沒出問題還好,要是遇到這樣的狀況,那就真的危險了。假如你不向上級請示,擅做主張,讓你增援的那個部隊是敵人的,布好陣,就埋伏你,這就要丟性命啊。
或者是上級讓你增援另外一個部隊,由於你離得最近,可是他不知道你不在那個位置,等你接到命令再趕到,友軍屍體都涼了
對應到Java中,就是保證了運行環境不會混亂。
由於Java類隨着他的類加載器一塊兒具有了一種帶有優先級的層次關係,
例如全部類的父類(除了它自己)"java.lang.Object",有了雙親委派,不管哪個類加載器加載,都是交給最頂層的啓動類加載器加載,這樣他在任何類加載器下都是那一個Object類。
若是沒有雙親委派,我本身定義一個Object類,放到程序的ClassPath中,那麼系統就會出現多個Object,那麼Java體系最基礎的行爲就都沒法保證了。
雙親委派的實現很是簡單
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
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.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
複製代碼
咱們以前說到,雙親委派模型只是一個推薦模型,並非強制約束,既然是推薦,那我能夠不聽啊。我就是要破壞,不過這裏的破壞並非貶義的,而是對其進行創新。一共大概有三次較大規模的"不聽"
雙親委派模型是JDK1.2以後纔有的,可是抽象類java.lang.ClassLoader
是1.0 就存在了的,面對已經存在的用戶自定義類加載器的實現代碼,Java設計者不得不作出一些妥協。java.lang.ClassLoader
中添加了一個findClass方法,原來用戶須要繼承java.lang.ClassLoader
類,而後重寫loadClass
方法,如今只須要重寫findClass
,把本身實現的加載邏輯放到這個方法裏,而不須要重寫loadClass
方法。
若是父類加載失敗,則調用本身的findClass
方法完成加載,這樣就能夠保證寫出來的加載器的邏輯還是符合雙親委派的。
雙親委派很好的解決了個各種加載器基礎類的同一問題,可是用戶又想調用用戶本身的代碼怎麼辦
好比咱們的JDBC,是各個廠商本身獨立實現的,Java只是提供一個接口,其餘廠商本身獨立實現,可是啓動類加載器可不認識這個東西。
因此Java設計者引入了一個不太優雅的設計:線程上下文類加載器
這個類加載器經過java.lang.Thread
類中的setContextClassLoader()
方法進行設置,若是建立線程時還未設置,他將會從父類線程中繼承一個,若是在應用程序的全局範圍內都沒有設置過,那這個類加載器默認就是應用程序類加載器
/** * Sets the context ClassLoader for this Thread. The context * ClassLoader can be set when a thread is created, and allows * the creator of the thread to provide the appropriate class loader, * through {@code getContextClassLoader}, to code running in the thread * when loading classes and resources. * * <p>If a security manager is present, its {@link * SecurityManager#checkPermission(java.security.Permission) checkPermission} * method is invoked with a {@link RuntimePermission RuntimePermission}{@code * ("setContextClassLoader")} permission to see if setting the context * ClassLoader is permitted. * * @param cl * the context ClassLoader for this Thread, or null indicating the * system class loader (or, failing that, the bootstrap class loader) * * @throws SecurityException * if the current thread cannot set the context ClassLoader * * @since 1.2 */
public void setContextClassLoader(ClassLoader cl) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("setContextClassLoader"));
}
contextClassLoader = cl;
}
複製代碼
有了這個線程上下文類加載器,JDBC服務使用這個線程上下文類加載器去加載所須要的SPI(Service Provider Interface,接口提供者)代碼,也就是父類加載器請求子類加載器去完成類加載的動做。這樣的行爲實際是打通了雙親委派模型的層次結構來逆向使用類加載器,違背了雙親委派的通常性原則。
Java中涉及SPI的加載基本都採用這種方式,如JNDI、JDBC、JCE和JBI
用戶對程序動態性的追求而致使的。
動態性:指的是當前一些很是熱門的名詞:代碼熱替換,模塊熱部署等等
簡單理解就是但願程序像計算機外設那樣,接上鼠標、U盤,不用重啓就能當即使用,鼠標有問題要換一個,也不須要關機、重啓。這樣的熱部署對企業級軟件開發者有很大的吸引力
像OSGi這種實現模塊化熱部署,已經算是無冕之王了,業界內的Java模塊化標準
他實現的關鍵是它自定義的類加載器機制。
OSGi下,類加載器再也不是雙親委派模型中的樹狀結構,而是進一步發展爲更加複雜的網狀結構,當收到類加載請求時,OSGi將按照下面的順序進行類搜索
java.*
開頭的類委派給父類加載器加載Fragment Bundle
中,若是在,則委派給Fragment Bundle
的類加載器加載Dynamic Import
列表的Bundle,委派給對應的Bundle的類加載器加載能夠根據實際項目來理解雙親委派模型,能夠參考我下面的博文
比較標準的雙親委派模型——tomcat
異類——OSGi