JVM-2

命名空間的補充說明

  1. 關於命名空間的補充說明

    1. 子加載器所加載的類可以訪問父加載器所加載的類
    2. 父加載器所加載的類無法訪問子加載器所加載的類
    3. 在jvm中是可以存在多個命名空間的,每個命名空間之間是相互隔離開的。命名空間裏面的類時相互不可見的。
    4. 在運行期,一個java類是由該類的完全限定名(binary name,二進制名)和用於加載該類的定義類加載器(defining loader)所共同決定的。如果同樣的名字(即相同的完全限定名)的類是由兩個不同的加載器所加載,那麼這些類就是不同的,即便.class文件的字節碼完全一樣,並且從相同的位置加載亦如此。
  2. 類加載器雙親委託模型的好處

    1. 可以確保java核心庫的類型安全:所有的java應用都至少會引用java.lang.Object類,也就是說在運行期 java.lang.Object這個類會被加載到java虛擬機中;如果這個加載過程是由java應用自己的類加載器所完成的,那麼很可能就會在jvm中存在多個版本的java.lang.Object類,而且這些類之間還是不兼容的,相互不可見的(正是命名空間在發揮着作用)。 藉助於雙親委託機制,java核心類庫中的類的加載工作都是由啓動類加載器統一來完成,從而確保了java應用所使用的都是同一個版本的java核心類庫,他們之間都是相互兼容的。
    2. 可以確保java核心類庫所提供的類不會被自定義的類所替代
    3. 不同的類加載器可以爲相同名稱(binary name)的類創建額外的命名空間。相同名稱的類可以並存在java虛擬機中,只需要用不同的類加載器來加載他們即可,不同類加載器所加載的類之間是不兼容的。這就相當於在java虛擬機內部創建了一個又一個相互隔離的java類空間,這類技術在很多框架中都得到了實際應用
  3. 先有雞還是先有蛋問題

    1. 我們知道對於加載器也是java類,那麼加載器又是由誰來加載的?
    2. 內建於JVM的啓動類加載器會加載java.lang.ClassLoader以及其他的java平臺類。當JVM啓動時,一塊特殊的機器碼會運行,他會加載擴展類加載器與系統類加載器 這塊特殊的機器碼就叫做啓動類加載器(bootstrap)啓動類加載器並不是java類,而其他的加載器則是java類。啓動類加載器是特定於平臺的機器指令,他負責開啓整個加載過程。所有類加載器(除了啓動類加載器)都被實現爲java類,不過總歸要有一個組件來加載第一個java類加載器,從而讓整個加載過程能夠順利進行下去,加載第一個純java類加載器就是啓動類加載器的職責。啓動類加載器還會負責加載供JRE,正常運行所需的基礎組件,這包括java.util與java.lang包中的類等等
  4. 線程上下文類加載器分析和實現

    1. 線程上下文類加載器

      • 當前類加載器(Current Classloader) 用於加載當前類的類加載器
      • 每一個類都會使用自己的類加載器(即加載自身的類加載器)來加載其他類(指的是這個類所依賴的其他類)如果ClassX引用了ClassY,那麼ClassX的類加載器就會去加載ClassY 前提的ClassY尚未被加載
      • 線程上下文類加載器是從JDK1.2開始引入的,類Thread中的getContextClassLoader()與setContextClassLoader(ClassLoader cl)分別用於獲取和設置上下文類加載器。
      • 如果沒有通過setContextClassLoader(ClassLoader cl)進行設置的話,線程將繼承其父線程的上下文類加載器。java應用運行時的初始線程的上下文類加載器,在線程中運行的代碼可以通過該類加載器來加載該類與資源。
    2. 線程上下文類加載器的重要性

      • SPI(Service Provider Interface)服務提供者接口 面臨父加載器看不到子加載器的類的問題父ClassLoader可以使用當前線程Thread.currentThread().getContextLoader()所指定的classloader加載的類這就改變了父ClassLoader不能使用子ClassLoader或是其他沒有直接父子關係的ClassLoader加載的類的情況,即改變了雙親委託模型。
      • 線程上下文類加載器就是當前線程的Current Classloader
      • 在雙親委託模型下,類加載時由下至上的,即下層的類加載器會委託上層進行加載,但是對於SPI來說,有些接口是由java核心庫說提供的,而java核心庫是由啓動類加載器來加載的,而這些接口的實現卻來自於不同的jar包(廠商提供),java啓動類加載器是不會加載其他來源的jar包,這樣傳統的雙親委託模型就無法滿足SPI的要求,而通過給當前線程設置上下類加載器,就可以由設置的上下文類加載器來實現對於接口實現類的加載。
    3. 線程上下類類加載的一般使用模式(獲取 - 使用 - 還原)

      1. 僞代碼:

        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            try {
                // targetTccl 你準備使用的類加載器
                Thread.currentThread().setContextClassLoader(targetTccl);
                // myMethod裏面則調用Thread.currentThread().getContextClassLoader(),獲取當前線程的上下文類加載器做某些事情
                myMethod();
            } finally {
                Thread.currentThread().setContextClassLoader(classLoader);
            }
            // 如果一個類由類加載器A加載,那麼這個類的依賴類也是由相同的類加載器加載的(如果該依賴類之前沒有被加載過的話)
            // ContextClassLoader的作用就是爲了破壞java的類加載的委託機制
            // 當高層提供了統一的接口讓低層去實現,同時又要在高層加載(或實例化)低層的類時,就必須要通過現場上下文類加載器來幫助高層的ClassLoader找到並加載該類
        
        
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章