SPI機制是如何規避雙親委派機制的?

来源:https://www.cnblogs.com/daihang2366/archive/2023/08/29/17665965.html
-Advertisement-
Play Games

# SPI是如何規避雙親委派機制的? # 1、何為雙親委派機制? > 雙親委派機制是什麼? 雙親委派機制指的是Java中類載入機制的特性。 > 雙親委派機制是作用於什麼地方? 雙親委派機制主要作用於類載入的時候。 > 類載入器 首先需要清晰的知道,雙親委派機制指的是類載入的特性。在瞭解其特性之前,我 ...


SPI是如何規避雙親委派機制的?

1、何為雙親委派機制?

雙親委派機制是什麼?

雙親委派機制指的是Java中類載入機制的特性。

雙親委派機制是作用於什麼地方?

雙親委派機制主要作用於類載入的時候。

類載入器

首先需要清晰的知道,雙親委派機制指的是類載入的特性。在瞭解其特性之前,我們需要先瞭解類載入器有哪些(不考慮自定義載入器的情況)。

載入器 解釋
BootStrap載入器 最為頂層的載入器,負責載入System.getProperty("sun.boot.class.path")下的Jar包,主要是jre\lib目錄下的內容。該類載入器為C實現,在Java中無法獲取
Ext類載入器 擴展類載入器,負責載入System.getProperty("java.ext.dirs")下的Jar包,主要是jre\lib\ext下的內容。在Java中對應ExtClassLoader(註意此處以jdk8為例,jdk11中有所改變)。
App類載入器 應用類載入器,負責載入System.getProperty("java.class.path")下的Jar包,主要是自身程式載入的包。在Java中對應AppClassLoader(註意此處以jdk8為例,jdk11中有所改變)。

類載入器之間的結構如何:

可以看出來,App類載入器是最小的一層,也是我們開發用戶接觸最多的一層,越往上載入的類就越核心。

雙親委派機制是什麼樣的結構?

雙親委派機制其實就是描述類載入器載入類的順序及其特點。

我們開發者需要去載入類的場景每天都在接觸,例如在代碼中new Car(我們自己的類),此時就是需要去載入這個類。在觸發載入類的時候,開發者處於載入器的最低層。那麼就可以看作成:App類載入器去載入Car這個類

而實際上的載入順序是這樣的:

App類載入器--通知-->Ext類載入器--通知-->BootStrap類載入器

BootStrap類載入器--發現找不到該類,則向下返回-->Ext類載入器--發現找不到該類,繼續向下返回-->App類載入器(當前類載入器如果找不到該類則拋出異常,否則載入成功)

上述為雙親委派機制載入類時的順序,其特點為先向上通知到最頂層,再由最頂層往下嘗試,直到成功載入或到達發送載入類請求的載入器。

這種載入特點最大的作用如下:

安全性:由於Java核心類均有BootStrap載入器、Ext載入器去載入,再加上這種載入類的特性,可以有效防止Java核心類被篡改,正常的Java應用無法修改核心類實現。不僅可以應用在Java核心類中,當我們的應用是插件式時,此方式也可以防止插件中篡改主程式的代碼。

2、SPI是什麼?

上面我們講述了雙親委派機制,現在要講述SPI。

SPI是什麼?

SPI(Service Provider Interface)是JDK內置的一種服務提供發現機制,可以用來啟用框架擴展和替換組件,主要是被框架的開發人員使用。

例如資料庫驅動中java.sql.Driver介面,其他不同廠商可以針對同一介面做出不同的實現,MySQL和PostgreSQL都有不同的實現提供給用戶,而Java的SPI機制可以為某個介面尋找服務實現。Java中SPI機制主要思想是將功能實現剔除到程式之外,這針對與模塊化解耦有很大的作用。

例如下圖:

除資料庫驅動以外,例如日誌框架、Dubbo等也涉及到SPI機制。

在上圖中,例如當我們需要具體Driver實現的時候,直接通過JDK的API:

ServiceLoader<java.sql.Driver> serviceLoader = ServiceLoader.load(java.sql.Driver.class);
for (java.sql.Driver driver : serviceLoader) {
     // mysql、pg、oracle、db2等
}

註意,SPI機制存在一些約定,這些約束如下:

  1. 三方介面需在META-INF/services/${interface_name}文件中列舉實現類,每一個實現類為一行。例如資料庫這,那麼示例如下:

    META-INF/services/java.sql.Driver

com.mysql.cj.jdbc.Driver
org.postgresql.Driver
oracle.jdbc.OracleDriver
com.ibm.db2.jcc.DB2Driver

​ 2.定義的實現類必須實現對應介面

​ 3.實現類必須提供無參構造器

3、為什麼說SPI規避了雙親委派機制?

​ 註意,我們前面說了雙親委派機制中,載入器會往上層載入器遞交載入請求,我們已知java.util.ServiceLoader的類載入器為BootStrap載入器。此載入器已經是最頂層,無更加上層的載入器。而按照載入器職責的約定,ServiceLoader所屬類載入器的職責是載入jdk核心類,其是無法載入到用戶的類。例如下圖:

​ 現在的問題是:既然ServiceLoader的類載入器是最頂層的,其載入職責不負責我們自己的類,那麼它是如何載入到類似JDBC這種實現類的呢?

附:ServiceLoader的類載入器是BootStrap類載入器,在程式中是無法獲取到該類的類載入器的。

4、SPI是如何規避雙親委派機制的?

​ 要搞清楚這個問題的原因,得先確認我們使用SPI的入口:

ServiceLoader<Xxxx> serviceLoader = ServiceLoader.load(Xxxx.class);

​ 進入該方法,尋找其實現的方式:

java.util.ServiceLoader#load(java.lang.Class)

​ 註意此處獲取了當前線程的類載入器,而線上程中調用該類方法的是我們用戶自己。那麼這裡就理解為獲取到了用戶的類載入器。

​ 再往該方法中查找,找到該段代碼:

java.util.ServiceLoader#ServiceLoader

​ 註意該段代碼中,cl為上一步獲取到的類載入器,如果發現類載入器不存在,會再次獲取系統預設載入器,這個系統預設載入器在常規情況下是用於載入啟動類的載入器(jdk註釋中解釋),而啟動類則是我們用戶自己定義的類,這裡毋庸置疑也會是應用類載入器。

​ 從上面的代碼中我們總結出來,ServiceLoader獲取了我們的應用類載入器,至此load方法入口基本上沒有其他內容可以細看。

​ 為減輕文章閱讀壓力,直接跳轉到該方法

java.util.ServiceLoader.LazyIterator#nextService

image

​ 註意這裡的loader是我們前面獲取到的應用類載入器,這個方法中是獲取到了具體需要實例化的實現類,即將對其進行實例化, 在這之前需要先獲取到Class,這裡使用Class.forName(class, false, ClassLoader)方法,這個方法的含義是使用指定的類載入器去載入指定的類。既然這裡的類載入器是應用類載入器,那麼類載入順序自然就又回到了應用類載入器-->擴展類載入器-->BootStrap類載入器-->擴展類載入器-->應用類載入器,能載入到我們想要的類也就不奇怪了。

​ 看到這裡也就明白了為什麼使用SPI仍然能正常載入類了。

​ SPI的載入機制看起來雖然方面,但仍然有缺點:

1. 無法實現動態載入、卸載的效果,只有最簡單的載入三方類的實現。
1. 由於實現原因,實現類必須提供無參構造器,局限性和擴展性很低

​ 綜合來說,SPI簡單但局限性大,項目中能接受這些缺點就可以放心使用,如接受不了則可以模擬SPI機制自行實現一套載入機制,自己實現起來擴展性和局限性肯定是原生SPI不能比的。

​ 本次內容結束,如發現內容錯誤請留言,會儘快改正。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • # 核心原理 長鏈接轉為短鏈接的核心原理是: 將短鏈接與原始長鏈接做一個映射,訪問短鏈接的時候,通過重定向的方式轉到長鏈接。 # 應用場景 比如分享功能,查看分享信息的原始鏈接通常是很長的,直接發給用戶,體驗不是很好,這時候就可以將其映射為一個短鏈接再發給用戶。 又比如我們熟知的百度網盤分享文件,雖 ...
  • ##一、定義 **講一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。建造者模式是一種創建型模式。** ##二、描述 **包含以下四個角色:** ![](https://img2023.cnblogs.com/blog/1780813/202305/1780813-202305 ...
  • 你想成為一名架構師,對嗎?別對我撒謊,我知道你想成為架構師。即使你不想,你還是想成為一名更好的開發者。否則,你就不會花時間閱讀這篇文章。 這種態度值得贊賞。畢竟,我們都希望在自己所從事的領域變得更好,即使不能稱為最好。我在這裡就是為了幫助你實現這一目標。 那麼,你如何成為一名架構師呢?當然是通過學習 ...
  • #### 本文為[李你幹嘛](https://www.cnblogs.com/liniganma)原創,轉載請註明出處:[Pybind11綁定C++抽象類(DLL介面)](https://www.cnblogs.com/liniganma/p/17666063.html) # 摘要 假設我們將DLL ...
  • 數據類型是編程中的重要概念。數據類型指定了變數值的大小和類型。 Go是靜態類型的,這意味著一旦變數類型被定義,它只能存儲該類型的數據。 Go有三種基本數據類型: - bool:表示布爾值,要麼是true,要麼是false。 - 數值型:表示整數類型、浮點數值和複數類型。 - string:表示字元串 ...
  • ### Java 8 的改進 - 速度更快 - 代碼更少(**Lambda表達式**) - 引入強大的**Stream API** - 便於並行 - 最大化減少空指針異常(**Optional**) - **Nashorn**引擎,允許在JVM上運行**js**應用 - **並行流**就是把一個內容 ...
  • 概述 Redis底層有六種數據類型包括:簡單動態字元串、雙向鏈表、壓縮列表、哈希表、跳錶和整數數組。這六種數據結構五大數據類型關係如下: String:簡單動態字元串 List:雙向鏈表、壓縮列表 Hash:壓縮列表、哈希表 Sorted Set:壓縮列表、跳錶 Set:哈希表、整數數組 數據類型和 ...
  • 背景: 根據以下簡單的代碼示例,我們將從源碼的角度分析其中的關鍵載入執行步驟,對pytest整體流程架構有個初步學習。 代碼示例: import pytest def test_add(): assert 1 + 1 == 2 def test_sub(): assert 2 - 1 == 1 通過 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...