JVM-執行緒上下文類載入器
SPI是什麼
Java提供了很多SPI,允許第三方為這些介面提供實現,最常見的SPI實現有JDBC、JNDI等等,根據類載入器的雙親委派模型,載入ServiceLoader的 BootstrapClassLoader 是不能載入SPI的實現類的,因為SPI的實現類是由 AppClassLoader 載入的,而 BootstrapClassLoader 是不能委派 AppClassLoader 來載入類的,那該怎麼辦呢?
SPI約定為:當服務的提供者提供了服務介面的一種實現之後,在jar包的META-INF/services/目錄裡同時建立一個以服務介面命名的檔案。該檔案裡就是實現該服務介面的具體實現類。而當外部程式裝配這個模組的時候,就能通過該jar包META-INF/services/裡的配置檔案找到具體的實現類名,並裝載例項化,完成模組的注入。
SPI就是
在resources目錄下,新建META-INF/services目錄,然後新建檔案(檔名=介面包名.介面名)。內容就是實現類的包名.類名
SPI的介面是Java核心庫的一部分,按照雙親委派模式和類載入器搜尋路徑而言:它是由啟動類載入器來載入的;但是SPI的實現類在我們應用引入之後在應用Classpath下,BootstrapClassLoader不認識它載入不了,只能由系統類載入器來載入的。原因在於啟動類載入器是無法找到 SPI 的實現類的(因為它只載入 Java 的核心庫),按照雙親委派模型,啟動類載入器又無法委派系統類載入器去載入類。也就是說,類載入器的雙親委派模式無法解決這個問題
這時候執行緒上下文類載入器排上了用場。執行緒上下文類載入器破壞了“雙親委派模型”,可以在執行執行緒中拋棄雙親委派載入鏈模式,使程式可以逆向使用類載入器。
執行緒上下文類載入器(TCCL)
可使用ServiceLoader的load方法獲取到TCCL
private static final String PREFIX = "META-INF/services/";
public static <S> ServiceLoader<S> load(Class<S> service) {
// 獲取當前呼叫執行緒的類載入器
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
Java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用來獲取和設定執行緒的上下文類載入器。如果沒有通過setContextClassLoader(ClassLoader cl)方法進行設定的話,執行緒將繼承其父執行緒的上下文類載入器。Java 應用執行的執行緒上下文類載入器初始是AppClassLoader,線上程中執行的程式碼可以通過此類載入器來載入類和資源。
ServiceLoader<Lxw> loader = ServiceLoader.load(Lxw.class);
Iterator<Lxw> iterator = loader.iterator();
while (iterator.hasNext()) {
Lxw l = iterator.next();
l.say();
}
應用程式啟動會走這個獲取到系統類載入器,是應用程式類載入器,然後執行第一行進入load放方法
獲取到執行緒上下文類載入器
,然後就是load了,new了一個serviceLoader
可以看到new了一個類出來
總結
-
直白一點說就是,我(JDK)提供了一種幫你(第三方實現者)載入服務(如資料庫驅動、日誌庫)的便捷方式,只要你遵循約定(把類名寫在/META-INF裡),那當我啟動時我會去掃描所有jar包裡符合約定的類名,再呼叫forName載入,但我的ClassLoader是沒法載入的,那就把它載入到當前執行執行緒的TCCL裡,後續你想怎麼操作(驅動實現類的static程式碼塊)就是你的事了。
-
執行緒上下文類載入器(它並不是一個真正的類載入器,而是通過當前執行緒拿到我們想要的類載入器->應用執行時它被放在了執行緒中,所以不管當前程式處於何處BootstrapClassLoader或ExtClassLoader等,在任何需要的時候都可以拿出去使用)。
執行緒上下文類載入器打破了雙親委派機制,實現逆向呼叫類載入器來載入當前執行緒中類載入器載入不到的類 -
當高層提供了統一介面讓低層去實現,同時又要是在高層載入(或例項化)低層的類時,比如上面spi的呼叫者ServiceLoader所在的BootstrapClassloader無法載入的時候,必須通過執行緒上下文類載入器來幫助高層的ClassLoader找到並載入該類。
-
類/介面是有名稱空間之分的,不同的類載入器是不同的名稱空間。一個類是由A類載入器載入的,那麼這個類的依賴類也會由這個A類載入器載入,但是如果所依賴的類不在A類載入器載入的範圍內,那麼就會找不到這個類。可以使用執行緒上下文類載入器進行載入使用。這種操作就是破壞了雙親委派模式
參考
- https://blog.csdn.net/yangcheng33/article/details/52631940
- https://anthonyzero.github.io/2019/10/01/JVM-%E7%BA%BF%E7%A8%8B%E4%B8%8A%E4%B8%8B%E6%96%87%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8/
相關文章
- JVM-類載入JVM
- JVM-類載入機制JVM
- JAVA-大白話探索JVM-類載入器(一)JavaJVM
- JVM-類載入子系統JVM
- JVM-虛擬機器執行子系統JVM虛擬機
- JAVA-大白話探索JVM-類載入過程(二)JavaJVM
- SingleThreadExecutor(單執行緒執行器)thread執行緒
- 虛擬機器執行子系統_類載入器、雙親委派模型虛擬機模型
- Java多執行緒(一)多執行緒入門篇Java執行緒
- JVM-執行時資料區之PC暫存器JVM
- Java 併發:執行緒、執行緒池和執行器全面教程Java執行緒
- Java中命名執行器服務執行緒和執行緒池Java執行緒
- 瀏覽器多執行緒和js單執行緒瀏覽器執行緒JS
- day20_多執行緒入門丶執行緒安全執行緒
- 多執行緒,執行緒類三種方式,執行緒排程,執行緒同步,死鎖,執行緒間的通訊,阻塞佇列,wait和sleep區別?執行緒佇列AI
- 瀏覽器執行緒瀏覽器執行緒
- java多執行緒之Thread類Java執行緒thread
- JAVA重點類 多執行緒Java執行緒
- Java多執行緒(二):Thread類Java執行緒thread
- Java多執行緒Thread類使用Java執行緒thread
- JavaSE_多執行緒入門 執行緒安全 死鎖 狀態 通訊 執行緒池Java執行緒
- 執行上下文
- go語言多執行緒入門筆記-執行緒同步Go執行緒筆記
- 多執行緒與高併發(一)多執行緒入門執行緒
- Java多執行緒學習(一)Java多執行緒入門Java執行緒
- 類載入器(JVM)JVM
- 類script標籤,非同步載入,順序執行非同步
- 多執行緒------執行緒與程式/執行緒排程/建立執行緒執行緒
- Java多執行緒入門Java執行緒
- 執行上下文和執行棧
- 探祕類載入器和類載入機制
- 類載入流程,類載入機制及自定義類載入器
- 啃碎併發(三):Java執行緒上下文切換Java執行緒
- Chrome、Edge瀏覽器內建多執行緒下載Chrome瀏覽器執行緒
- JVM入門--類載入器JVM
- 多執行緒下載工具 NeatDownloadManager下載執行緒
- Swift多執行緒之Operation:非同步載入CollectionView圖片Swift執行緒非同步View
- 多執行緒系列(二)之Thread類執行緒thread