SPI 機制-插件化擴展功能


SPI(Service Provider Interfaces),中文直譯服務提供者接口,一種服務發現機制。可能很多人都不太熟悉這個機制,但是平常或多或少都用到了這個機制,比如我們使用 JDBC 連接操作數據庫的時候。

SPI 主要適用於功能擴展的場景,如一些框架提供某一部分功能可以由第三方開發人員擴展,滿足其自身業務需求。

假設我們在公司內實現了一個統一登陸框架,框架內部僅僅提供用戶名/密碼登陸方式。后來 A 部門想使用該框架,但是他們想增加微信登陸授權。正常情況下,我們可以改動登陸框架代碼,增加微信登陸實現方式。如果后面又增加 QQ 登陸,淘寶登陸那?也只能不斷相應的實現。

SPI 實現方式

這種情況如果使用 SPI,可以在不用改動框架代碼前提下,增加新的登陸實現方式。下面用代碼演示如何使用 SPI。

定義接口

首先我們新建一個 maven 項目 oauth-api,在這個項目創建一個公共接口。

public interface OauthLoginService {
    void login();
}

第三方實現該接口

再新建一個 maven 項目 wechat-oauth ,引入上面 oauth-api 依賴

public class WechatLoginService implements OauthLoginService {
    @Override
    public void login() {
        System.out.println("使用微信登陸授權");
    }
}

定義配置文件

SPI 需要將接口實現定義在配置文件中,文件名為接口全名稱,如 com.andyxh.OauthLoginService,配置文件需放在 resources\META-INF\services 文件夾下。文件內容如下:

com.another.WechatLoginService

加載接口實現類

新建 maven 項目 oauth-login,在這個項目中引入 wechat-oauthoauth-api 依賴。SPI 核心將會使用 java.util.ServiceLoader讀取上面上面定義配置文件,加載所有服務實現類。使用代碼如下:


ServiceLoader<OauthLoginService> serviceLoader=ServiceLoader.load(OauthLoginService.class);
serviceLoader.forEach(OauthLoginService::login);

打印結果:

使用微信登陸授權

SPI 實際應用

上面說過 JDBC 中使用到 SPI 進制。 JDK 定義標准數據庫接口,相應的數據庫廠商實現這類接口。以 mysql-connector-javal 為例。

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
        </dependency>

mysql jar 包 META-INF/services 中存在 java.sql.Driver 文件,這個文件定義了實現類。

image.png

com.mysql.cj.jdbc.Driver

可以看到 java.sql.Driver 是標准 SPI 接口,而 com.mysql.cj.jdbc.Driver 是 mysql 標准實現接口。

何時加載 java.sql.Driver

我們將會使用 DriverManager.getConnection 獲取相應數據庫連接。這個類內部存在一個靜態代碼塊,將會使用 ServiceLoader 加載實現類。

    static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }

 private static void loadInitialDrivers() {
        ....
	 ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);  
	 Iterator<Driver> driversIterator = loadedDrivers.iterator();  
  	 try{  
		 while(driversIterator.hasNext()) {  
		 driversIterator.next();  
	 }  
	 } catch(Throwable t) {  
		 // Do nothing  
	 }  
		 return null;  
	 }
	....
    }

Java SPI 存在問題

ServiceLoader 一次性將會實例化所有實現,但是如果沒有某一擴展初始化耗時很久,但是卻不需要立刻使用,就會非常浪費資源。

基於這個問題, Dubbo SPI 機制改進 Java SPI 的不足,做到按需加載並且增加 ioc 與 aop 的功能,下篇文章可以在具體聊聊,敬請期待。
其他平台.png


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM