深入理解《java 7 核心技術和最佳實踐》讀書筆記--(5)

類加載器的根本作用,即從包含字節代碼的字節流中定義出虛擬機中的Class類對象。

一個java類被加載之後,可以通過Class對象的getClassLoader方法獲取加載它的類加載器對象。也可以通過繼承ClassLoader類實現自己的類加載器。ClassLoader除了可以加載java類之外,還可以加載相關的文件資源

  • loadClass方法,參數爲java類名稱,返回值爲Class對象
  • defineClass方法用於,從字節代碼中定義出Java類Class對象,當使用類加載器對象的loadClass方法加載一個java類時,則稱這個類加載器對象爲該Java類的初始類加載器,當成功得到一個Class對象,那麼虛擬機就會記錄類加載器對象和他加載的Class對象之間的關聯記錄下來,在加載其他java類時,會首先查詢這個緩存關係

自定義類加載器

創建自定義類加載器,只需要繼承ClassLoader即可,覆寫其中某些方法。

  • defineClass 爲 final ,不允許覆寫
  • protected Class<?> loadClass(String name, boolean resolve):該方法包含默認的雙親類加載器優先策略,當不需要雙親優先策略可以覆寫該類
  • findLoadedClass:類加載器過程中,會記錄已經加載的java類的類加載器,用於緩存
  • findClass: 當通過代理模式無法使用雙親類加載器成功加載java類時,findClass方法會調用,比如定義從遠程url中加載class文件
  • resolveClass: 鏈接一個定義好的Class類的對象

自定義累加器主要用途

  • 對java類的字節代碼進行特殊的查找和處理,如java字節碼存在遠程服務器,或字節碼經過加密處理
  • 利用類加載器產生的隔離特性滿足特殊需求

線程上下文類加載器

Thread類有兩個方法用來獲取和設置當前線程的上下文類加載器。如果一個線程沒有顯示設置其上下文類加載器,則使用其父線程的上下文類加載器。程序啓動的第一個線程的上下文類加載器 默認是 系統類加載器

上下文類加載器,主要是方便共享classLoader,一個使用場景是SPI?

關於SPI 與 線程上下文加載器 ,推薦這篇文章 Java線程上下文加載器與SPI

看完文章,給我的理解就是,由於SPI 的接口一般都是由啓動類加載器加載,而SPI實現類都是由 系統類加載器加載,如果還是默認雙親模式,那麼接口無法加載這些實現類,而服務發現機制,讓接口可以發現實現類 並加載,即擴大了class加載的範圍?

Class.forName

Class.forName方法與 ClassLoader類區別在於Class.forName方法可以初始化java類。意味着Java類中的靜態變量會初始化,靜態代碼塊也會被執行!

Class.for("xx.yy.zz");//會初始化java類,靜態代碼塊也會執行

classLoader.loadClass("xx.yy.zz");//不會執行靜態代碼塊

加載資源

類加載器 除了可以加載Java類以外,還可以加載和Java類相關的資源文件。使用類加載器來加載資源的好處是可以解決資源文件的存放路徑的不固定性。不需要像Java 文件API那樣需要絕對路徑。

  • getResouce:類似於loadClass方法,也會有類似雙親委託的機制。如果通過委託找不到資源,就調用findResource方法來查找
public URL getResource(String var1) {
        URL var2;
        if (this.parent != null) {
            var2 = this.parent.getResource(var1);
        } else {
            var2 = getBootstrapResource(var1);
        }

        if (var2 == null) {
            var2 = this.findResource(var1);
        }

        return var2;
    }

  • findResouce方法類似於findClass方法。如果類加載器有自定義的資源加載策略,需要覆寫該方法
  • getResources()方法,這個方法會返回所有具有該名稱的資源文件

使用classLoader 查找資源需要書寫資源所在的包名

Class類也有相關獲取資源的方法,方法名也爲getResource 和 getResourceAsStream.但會使用資源名稱轉化。如果資源名稱以‘/’開頭,則會去掉 ‘/;
否則自動在資源名稱前加上class類對象所在包的名稱

類似 

ReadFile file = new ReadFile();
ClassLoader classLoader = file.getClass().getClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream("META-INF/spring.factories");
Properties p1 = new Properties();
p1.load(resourceAsStream);
System.out.println(p1.getProperty("org.springframework.boot.autoconfigure.EnableAutoConfiguration"));

InputStream resource = file.getClass().getResourceAsStream("/META-INF/spring.factories");
Properties p2 = new Properties();
p2.load(resource);
System.out.println(p2.getProperty("org.springframework.boot.autoconfigure.EnableAutoConfiguration"));

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章