Dubbo剖析-增強SPI的實現

weixin_34365417發表於2018-03-20

一、前言

在Duboo剖析-整體架構分析中介紹了dubbo中除了Service 和 Config 層為 API外,其他各層均為SPI,為SPI意味著下面各層都是元件化可以被替換的,這也是dubbo比較好的一點。

二、JDK中標準SPI

JDK 中的 SPI(Service Provider Interface)是面向介面程式設計的,服務規則提供者會在 JRE 的核心 API 裡面提供服務訪問介面,而具體實現則由其他開發商提供。

例如規範制定者在rt.jar包裡面定義了 資料庫 的驅動介面 java.sql.Driver。 MySQL 實現的 Jar,如下:

5879294-7c93099c1247186f
screenshot.png
public class com.mysql.jdbc.Driver extends com.mysql.jdbc.NonRegisteringDriver implements java.sql.Driver

下面我們寫個測試程式碼,看看具體是如何工作的。

    public static void main(String[] args) {
               //(1)
        ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class);
               //(2)
        Iterator<Driver> iterator = loader.iterator();
        while (iterator.hasNext()) {
            Driver driver = (Driver) iterator.next();
            System.out.println("driver:" + driver.getClass() + ",loader:" + driver.getClass().getClassLoader());
        }
        //(3)
        System.out.println("current thread contextloader:" + Thread.currentThread().getContextClassLoader());
               //(4)
        System.out.println("ServiceLoader loader:" + ServiceLoader.class.getClassLoader());
    }

}

然後引入 MySQL 驅動的 Jar 包,執行結果如下。

driver:class com.mysql.jdbc.Driver,loader:sun.misc.Launcher$AppClassLoader@4554617c
current thread contextloader:sun.misc.Launcher$AppClassLoader@4554617c
ServiceLoader loader:null

可知找到了mysql的驅動,如果你在引入Oracle的驅動的jar包後在執行,則會輸出找到了mysql和Oracle的驅動,這也說明了,JDK標準的SPI會同時把spi介面的所有的實現類都提前載入好。

關於JDK中SPI的原理和具體使用可以參考 Java 類載入器揭祕
一種特殊的類載入器 ContextClassLoader 章節。

三、Dubbo增強的SPI

Dubbo 的擴充套件點載入是基於JDK 標準的 SPI 擴充套件點發現機制增強而來的,Dubbo 改進了 JDK 標準的 SPI 的以下問題:

  • JDK 標準的 SPI 會一次性例項化擴充套件點所有實現,如果有擴充套件實現初始化很耗時,但如果沒用上也載入,會很浪費資源。

  • 如果擴充套件點載入失敗,就失敗了,給使用者沒有任何通知。比如:JDK 標準的ScriptEngine,如果Ruby ScriptEngine 因為所依賴的 jruby.jar 不存在,導致 Ruby ScriptEngine 類載入失敗,這個失敗原因被吃掉了,當使用者執行 ruby 指令碼時,會報空指標異常,而不是報Ruby ScriptEngine不存在。

  • 增加了對擴充套件點 IoC 和 AOP 的支援,一個擴充套件點可以直接 setter 注入其它擴充套件點。

下面看看Dubbo增強的SPI實現的時序圖:

5879294-b69cdfc520e912dd.png
image.png
  • 其中程式碼(1)獲取當前SPI介面對應的ExtensionLoader
  • 程式碼(2)獲取介面卡例項,內部首先獲取該spi對應的所有實現類的Class物件,然後建立介面卡例項,最後注入該介面卡依賴的其他擴充套件點。
  • 程式碼(8)根據名稱獲取具體的spi實現類,內部是建立一個實現類的例項,並使用warp類進行包裝後返回。

四、總結

本文簡單的介紹了Dubbo中SPI實現原理,更詳盡的解析 敬請期待

相關文章