天天看點

Java SPI 與 Dubbo SPI

SPI(Service Provider Interface)是JDK内置的一種服務提供發現機制。本質是将接口實作類的全限定名配置在檔案中,并由服務加載器讀取配置檔案,加載實作類。這樣可以在運作時,動态為接口替換實作類。

在Java中SPI是被用來設計給服務提供商做插件使用的。基于政策模式來實作動态加載的機制。我們在程式隻定義一個接口,具體的實作交個不同的服務提供者;在程式啟動的時候,讀取配置檔案,由配置确定要調用哪一個實作。有很多元件的實作,如日志、資料庫通路等都是采用這樣的方式,最常用的就是 JDBC 驅動。

1.  Java SPI

核心類:java.util.ServiceLoader

Java SPI 與 Dubbo SPI

服務是一組衆所周知的接口和(通常是抽象的)類。服務提供者是服務的特定實作。提供者中的類通常實作接口,并子類化服務本身中定義的類。服務提供者可以以擴充的形式安裝在Java平台的實作中,即放置在任何常見擴充目錄中的jar檔案。提供程式也可以通過将它們添加到應用程式的類路徑或其他特定于平台的方法來提供。

通過在資源目錄META-INF/services中放置一個提供程式配置檔案來識别服務提供程式。檔案名是服務類型的完全限定二進制名稱。該檔案包含具體提供程式類的完全限定二進制名的清單,每行一個。每個名稱周圍的空格和制表符以及空白行将被忽略。注釋字元是'#';在每一行中,第一個注釋字元之後的所有字元都将被忽略。檔案必須用UTF-8編碼。

按照上面的方法,我們來寫個例子試一下

首先,定義一個接口Car

兩個實作類

ToyotaCar.java

HondaCar.java

在META-INF/services下建立一個名為org.example.Car的文本檔案

最後,寫個測試類運作看一下效果

Java SPI 與 Dubbo SPI

跟一下ServiceLoader的代碼,看看是怎麼找到服務實作的

Java SPI 與 Dubbo SPI

用目前線程的類加載器加載

Java SPI 與 Dubbo SPI

接口和類加載器都有了,萬事俱備隻欠東風

Java SPI 與 Dubbo SPI

Java SPI 不足之處:

不能按需加載。Java SPI在加載擴充點的時候,會一次性加載所有可用的擴充點,很多是不需要的,會浪費系統資源

擷取某個實作類的方式不夠靈活,隻能通過 Iterator 形式擷取,不能根據某個參數來擷取對應的實作類

不支援AOP與IOC

如果擴充點加載失敗,會導緻調用方報錯,導緻追蹤問題很困難

2.  Dubbo SPI

Dubbo重新實作了一套功能更強的SPI機制, 支援了AOP與依賴注入,并且利用緩存提高加載實作類的性能,同時支援實作類的靈活擷取。

核心類:org.apache.dubbo.common.extension.ExtensionLoader

先來了解一下@SPI注解,@SPI是用來标記接口是一個可擴充的接口

改造一下前面的例子,在Car接口上加上@SPI注解

兩個實作類不變

在META-INF/dubbo目錄下建立名為org.example.Car的文本檔案,内容如下(鍵值對形式):

編寫測試類

下面跟一下代碼

Java SPI 與 Dubbo SPI
Java SPI 與 Dubbo SPI

如果緩存Map中有,直接傳回,沒有則加載完以後放進去

Java SPI 與 Dubbo SPI
Java SPI 與 Dubbo SPI
Java SPI 與 Dubbo SPI
Java SPI 與 Dubbo SPI
Java SPI 與 Dubbo SPI
Java SPI 與 Dubbo SPI

加載政策到底是怎樣的呢?

Java SPI 與 Dubbo SPI
Java SPI 與 Dubbo SPI
Java SPI 與 Dubbo SPI

到這裡就有點明白了,又看到了熟悉的ServiceLoad.load(),這不是剛才講的Java SPI嘛

Java SPI 與 Dubbo SPI
Java SPI 與 Dubbo SPI
Java SPI 與 Dubbo SPI
Java SPI 與 Dubbo SPI

回到之前政策那個地方,将政策按順序排列,依次周遊所有的政策來加載。就是在那三個目錄下查找指定的檔案,并讀取其中的内容

Java SPI 與 Dubbo SPI

跟之前的ServiceLoader如出一轍

Java SPI 與 Dubbo SPI

遇到@Adaptive标注的就緩存起來

Java SPI 與 Dubbo SPI

下課