1. 程式人生 > 其它 >2022/4/13 JavaDay05

2022/4/13 JavaDay05

類載入  

  Java是執行在Java的虛擬機器(JVM)中的,在初步學習Java時,我們都知道,編寫的Java原始碼會被編譯器編譯成.class的位元組碼檔案。然後ClassLoader負責將這些class檔案給載入到JVM中去執行

JVM中提供了三層的ClassLoader:

  • Bootstrap classLoader:主要負責載入核心的類庫(java.lang.*等),構造ExtClassLoader和APPClassLoader。
  • ExtClassLoader:主要負責載入jre/lib/ext目錄下的一些擴充套件的jar。
  • AppClassLoader:主要負責載入應用程式的主函式類

雙親委派機制
  通俗點講,雙親可以理解為父類,當.class檔案需要被jvm進行載入的時候,如果一個類載入器收到了類載入的請求,它首先不會自己去嘗試載入這個類,而是把這個請求委派給父類載入器去完成。只有當父類載入器無法載入的時候(比如範圍超過可搜尋區域),當前載入器才會執行父類的載入;

  需要注意的是,自定義載入器在“優先順序”上,是最低的,所以簡單來講,自定義的classLoader去載入自己的類,一定是父級們都沒有辦法載入的情況下。


為什麼要使用雙親雙親委派機制:
  1.為了防止核心類被篡改

    使用者自己編寫了一個稱為java.lang.Object的類,在沒有雙親委派模型的情況下,將會用自定義的類載入器載入,那系統中將會出現多個不同的Object類,Java型別體系中最基礎的行為也就無法保證,應用程式也將會變得一片混亂。

  2.防止類的重複載入

 

打破雙親委派模型的歷史
1.第一次破壞

  由於雙親委派模型是在JDK1.2之後才被引入的,而類載入器和抽象類java.lang.ClassLoader則在JDK1.0時代就已經存在,面對已經存在的使用者自定義類載入器的實現程式碼,Java設計者引入雙親委派模型時不得不做出一些妥協。

在此之前,使用者去繼承java.lang.ClassLoader的唯一目的就是為了重寫loadClass()方法,這是因為虛擬機器在進行類載入的時候會呼叫載入器的私有方法loadClassInternal(),而這個方法唯一邏輯就是去呼叫自己的loadClass()。

使用者重寫了loadClass才能實現自己的類載入邏輯。

2.第二次破壞

雙親委派模型的第二次“被破壞”是由這個模型自身的缺陷所導致的,雙親委派很好地解決了各個類載入器的基礎類的同一問題:越基礎的類由越上層的載入器進行載入

基礎類之所以稱為“基礎”,是因為它們總是作為被使用者程式碼呼叫的API,但世事往往沒有絕對的完美。

如果基礎類又要呼叫回用戶的程式碼,那該麼辦?

一個典型的例子就是JNDI服務,JNDI現在已經是Java的標準服務,它的程式碼由啟動類載入器去載入(在JDK1.3時放進去的rt.jar),但JNDI的目的就是對資源進行集中管理和查詢,它需要呼叫由獨立廠商實現並部署在應用程式的ClassPath下的JNDI介面提供者的程式碼,但啟動類載入器不可能“認識”這些程式碼。

為了解決這個問題,Java設計團隊只好引入了一個不太優雅的設計:執行緒上下文類載入器(Thread Context ClassLoader)。這個類載入器可以通過java.lang.Thread類的setContextClassLoader()方法進行設定,如果建立執行緒時還未設定,他將會從父執行緒中繼承一個,如果在應用程式的全域性範圍內都沒有設定過的話,那這個類載入器預設就是應用程式類載入器。

有了執行緒上下文載入器,JNDI服務就可以使用它去載入所需要的SPI程式碼,也就是父類載入器請求子類載入器去完成類載入的動作,這種行為實際上就是打通了雙親委派模型層次結構來逆向使用類載入器,實際上已經違背了雙親委派模型的一般性原則,但這也是無可奈何的事情。Java中所有涉及SPI的載入動作基本上都採用這種方式,例如JNDI、JDBC、JCE、JAXB和JBI等。

3.第三次破壞

  熱部署、無感升級等新功能,不需要程式重啟即可將.class檔案載入進jvm,這個過程中會破壞雙親委派模型


參考連結:https://blog.csdn.net/weixin_43889841/article/details/112170013