天天看點

類加載機制2——類加載器

前置知識

類加載器是通過類的全限定名,來擷取類的二進制位元組流的代碼。

類加載機制:jvm把class資料加載到記憶體,并對資料進行驗證、準備、解析、初始化,最終形成可以被虛拟機使用的java類。

類的預設加載器,通過雙親委派機制進行類的加載。

加載類的加載器和類本身一起确定類的唯一性,若同一個class檔案,被不同的加載器加載,則是不同的類。

雙親委派機制

類加載機制2——類加載器

啟動類加載器:主要負責加載java的核心類庫,/lib目錄下的rt.jar、resources.jar、charsets.jar和class等

擴充類加載器:主要負責加載java擴充包中的類庫,/lib/ext 目錄下的jar包和class檔案

應用類加載器:主要負責加載目前應用的classpath下的所有類

自定義加載器:加載指定路徑的class檔案

  當一個加載器收到加載一個類的請求時,會先把該請求委派給自己的父類加載器執行加載,故所有的類加載都會被委派到啟動類加載器中,若父類加載器加載失敗,才會自己嘗試加載。

  在classloader類中的loadclass方法中實作

  

類加載機制2——類加載器

  以上源代碼的邏輯為:

先檢查類是否已經加載過

若類還未加載,則檢查父類加載器是否存在,若存在,則調用父類加載器的loadclass方法進行加載;若不存在,則用啟動加載器加載。

若父類加載器加載失敗,則嘗試自己加載,調用自己的加載方法findclass

使類跟類加載器一樣擁有更嚴格的層級關系。如object類,無論在哪裡使用,最終都會被委派給啟動類加載器加載,保證object類在程式的各種類加載器環境中,都是同一個類。

如果沒有使用雙親委派模型,可以由各個類加載器自行加載的話,如果使用者自定義了一個名為java.lang.object類,放在程式的classpath中,那系統中就會出現多個不同的object類,導緻應用程式執行的混亂。

  通過構造器注入parent

類加載機制2——類加載器

  自定義一個類加載器,繼承抽象類classloader,重寫loadclass方法,使其不進行雙親委派。

loadclass:預設實作的雙親委派機制

findclass:加載類class位元組碼,是目前類加載器的加載方法,若想自定義的類加載器也遵守雙親委派機制,則隻需要重寫findclass方法

defineclass:将類的位元組碼轉換成class對象

   1、第1次是jdk1.2之前,那時已經有了類加載器和classloader類,但是不是雙親委派機制的

   2、第2次是模型自身的缺陷導緻的,雙親委派機制使得越基礎的類越由上層的加載器進行加載,正常情況下,使用者代碼繼承、調用基礎類,雙親委派機制加載沒有問題。但是若從基礎類中調回使用者代碼,則在上層加載器中,是無法找到下層的應用代碼的,此時就需要破壞雙親委派機制,java中由基礎類調用spi接口的地方都會如此。

   spi接口:service provider interface,如jndi、jdbc等。其本質是面向接口程式設計,具體實作或擴充由第三方在應用程式中實作。

   在上層類加載器中無法加載到應用程式中實作的具體類,如何解決這個問題?

   通過線程上下文類加載器實作contextclassloader。由thread類的setcontextclassloader方法設定,若未設定,則會繼承父線程的類加載器。在應用程式中,若沒有設定過這個值,則預設為應用程式類加載器。

    

類加載機制2——類加載器

    是以,在調用spi接口時,可以通過getcontextclassloader擷取線程上下文加載器,通過應用程式加載器去加載所需的spi服務類。這是一種父類加載器去請求子類加載器完成類加載的過程,破壞了雙親委派機制。如drivermanager中的實作

        

類加載機制2——類加載器

  3、第3次破壞雙親委派機制是為了追求程式的動态性,如代碼的熱替換,程式的熱部署等,即代碼替換或子產品替換後,不需要重新開機即可生效。

    這種實作是基于自定義類加載器實作的,此時加載器不再是有上下層級的樹狀結構,而是一個網狀結構,每一個子產品都有一個自定義的加載器,每當要替換掉一個子產品時,就會将子產品和類加載器都一起替換掉,以實作代碼的熱替換。   

參考書籍:《深入了解java虛拟機》第3版,作者:周志明