前言
在討論什麼是SPI前,先回憶一下什麼是API
什麼是API?
當實現方提供了介面和實現,我們可以透過呼叫實現方的介面從而擁有實現方給我們提供的能力,這就是 API。
這種情況下,介面和實現都是放在實現方的包中。呼叫方透過介面呼叫實現方的功能,而不需要關心具體的實現細節。
什麼是SPI?
當介面存在於呼叫方這邊時,這就是**SPI **。由介面呼叫方確定介面規則,由不同的廠商根據這個規則對介面進行實現,從而提供服務。
舉個例子:
API:我是一個老闆,我需要達到一個目標,然後我去找能幫我達到這個目標的實現方(多個),至於怎麼實現的,我不管,我只看結果,也就是說介面和介面實現都由實現方來制定
SPI:我是一個老闆,我需要達到一個目標,然後我去找能幫我達到這個目標的實現方,我制定一個實現標準,多個實現方根據老闆
提供的實現標準去實現,也就是說介面是由老闆(呼叫方)制定,而介面實現由實現方來制定
JDBC中的SPI
在 JDBC 使用過程中,有註冊驅動這一步驟,程式碼如下:
Class.forName("com.mysql.jdbc.Driver");
現在,我們將那個案例中的這行程式碼給註釋/刪除掉。即 getConnection() 方法程式碼如下:
public static Connection getConnection() { Connection conn = null; //try { //Class.forName("com.mysql.jdbc.Driver"); try { conn = DriverManager.getConnection(JDBC_URL, USERNAME, PASSWORD); System.out.println(conn); } catch (SQLException e) { System.out.println("資料庫連線失敗"); } //} catch (ClassNotFoundException e) { //System.out.println("驅動包不存在"); //} return conn; }
會發現,Connection還是能夠獲取成功的,那為什麼 JDBC 中不註冊驅動還能連線資料庫成功?就是SPI機制的作用了
看一下DriverManger的原始碼
public class DriverManager { private DriverManager(){} static { loadInitialDrivers(); println("JDBC DriverManager initialized"); } ... }
在loadInitialDrivers()方法中:
會去查詢 Driver 介面的服務類,檔案路徑是:META-INF/services/java.sql.Driver。建立檔案裡面的實現類
開啟IDEA驗證一下,去 External Libraries 找到我們的資料庫依賴檢視,確實如此
SPI機制簡化了應用程式的程式碼,並提高了靈活性和可擴充套件性
透過SPI機制,不同的資料庫供應商可以提供自己的驅動程式,而應用程式可以輕鬆地與這些不同的資料庫進行互動。
Dubbo中的SPI
根據上面的案例,SPI的執行機制已經清楚了,再來看一下Dubbo中哪裏用到了SPI
Dubbo SPI 邏輯封裝在 ExtensionLoader 類,透過 ExtensionLoader,可載入指定實現類。
Dubbo SPI 所需配置檔案需放置在 META-INF/dubbo 這個路徑下,同樣開啟IDEA驗證一下
舉個例子,專案中需要配置不同的序列化器,由你自己(實現方)去實現由Dubbo(呼叫方)制定出來的介面