Java/Spring/Dubbo三種SPI機制,到底誰更好?

SPI 機制應(yīng)用在了大家項目中的很多地方,在很多框架中也有普遍應(yīng)用,只不過很多人并沒有感知。

舉個例子,為什么我們在項目中引入 mysql-connector 的 jar 包,就可以直接連接 MySQL 數(shù)據(jù)庫了?

本篇文章就來介紹一下 SPI,聊聊 Java 、Spring、Dubbo 中的 SPI 機制。

SPI

SPI ( Service Provider Interface),是一種服務(wù)發(fā)現(xiàn)機制。

SPI 的本質(zhì)是將接口的實現(xiàn)類的全限定名配置在文件中,并由服務(wù)加載器讀取配置文件,加載對應(yīng)接口的實現(xiàn)類。這樣就可以在運行時,獲取接口的實現(xiàn)類。

通過這一特性,我們可以很容易地通過 SPI 機制為程序提供拓展功能。

Java SPI

Java SPI 機制:

Java SPI 是“基于接口的編程+策略模式+配置文件”組合實現(xiàn)的動態(tài)加載機制。

設(shè)計一個接口,將接口的實現(xiàn)類寫在配置文件中,服務(wù)通過讀取配置文件來發(fā)現(xiàn)實現(xiàn)類,進(jìn)行加載實例化然后使用。

配置文件路徑:classpath下的META-INF/services/

配置文件名:接口的全限定名

配置文件內(nèi)容:接口的實現(xiàn)類的權(quán)限定名

應(yīng)用舉例:

1、自定義接口

// 接口
public interface Superman {
void introduce();
}

// 實現(xiàn)類1
public class IronMan implements Superman{
@Override
public void introduce() {
System.out.println("我是鋼鐵俠!");
}
}
// 實現(xiàn)類2
public class CaptainAmerica implements Superman {
@Override
public void introduce() {
System.out.println("我是美國隊長!");
}
}

配置文件:

測試:

public static void main(String[] args) {
ServiceLoader<Superman> serviceLoader = ServiceLoader.load(Superman.class);
System.out.println("Java SPI:");
serviceLoader.forEach(Superman::introduce);
}

運行結(jié)果:

2、java.sql.Driver 接口

MySQL 的實現(xiàn):

看到這里,你應(yīng)該就知道開頭問題的答案了,mysql-connector 的 jar 表中正是通過 SPI 的方式實現(xiàn)了 java 的 Driver 接口,所以我們的服務(wù)可以在運行時獲取到 mysql 的驅(qū)動類,從而連接 mysql 。

Java SPI 原理:

Java SPI 地實現(xiàn)在 ServiceLoader 類:

這里截取部分代碼,有興趣的同學(xué)自行閱讀。

獲取prefix下的配置文件(包括 jar 包):

Java SPI 總結(jié):

Java SPI 機制:為某個接口發(fā)現(xiàn)/尋找服務(wù)實現(xiàn)的機制。

優(yōu)點:

核心思想:解耦,使得第三方服務(wù)模塊的裝配控制的邏輯與調(diào)用者的業(yè)務(wù)代碼分離??梢愿鶕?jù)實際業(yè)務(wù)情況進(jìn)行使用或擴展。

缺點:

1、獲取接口的實現(xiàn)類的方式不靈活
serviceLoader 只能通過 Iterator 形式遍歷獲取,不能根據(jù)參數(shù)獲取指定的某個實現(xiàn)類。

2、資源浪費

serviceLoader 只能通過遍歷的方式將接口的實現(xiàn)類全部獲取、加載并實例化一遍。如果不想用某些實現(xiàn)類,它也會被加載并實例化,造成浪費。

Spring SPI

與 JDK SPI 類似, 相對于 Java 的 SPI 的優(yōu)勢在于:

Spring SPI 指定配置文件為 classpath 下的 META-INF/spring.factories,所有的拓展點配置放到一個文件中。

配置文件內(nèi)容為 key-value 類型,key 為接口的全限定名, value 為實現(xiàn)類的全限定名,可以為多個。

spring.factories 文件舉例:

應(yīng)用舉例:

以 dubbo 的使用舉例:

為什么我們在項目中引入 dubbo jar 包,application.yml 中配置 registry,provider 等,就可以直接通過使用 dubbo 的 Service 注解和 Reference 注解來使用 dubbo 服務(wù)了?

哪有什么歲月靜好,不過是有人替你負(fù)重前行?!柏?fù)重前行的人”就是 "dubbo-spring-boot-starter" 。其實就是用到了 spring SPI :

以 EnableAutoConfiguration 的實現(xiàn)類 DubboAutoConfiguration 為例:

在 spring boot 啟動過程中 ,在 SpringFactoriesLoader.loadFactoryNames(type, classLoader) 這一步中會將 EnableAutoConfiguration 的實現(xiàn)類全部進(jìn)行加載、解析、初始化。

在實例化 EnableAutoConfiguration 的實現(xiàn)類時,會執(zhí)行實現(xiàn)類 dubboAutoConfiguration 中的具體邏輯,將 dubbo 服務(wù)器啟動并注冊到 spring 容器中。

DubboAutoConfiguration的大概實現(xiàn):

讀取配置文件中的配置項值(配置項:DubboConfigConfiguration)生成多個配置 bean,掃描 dubbo @Service 和 @Reference 注解的類,生成對應(yīng)的 bean。

其實在我們使用的第三方依賴包中,很多都使用了 Spring SPI,如 dubbo,mybatis,redisson 等等。

Dubbo SPI

dubbo的 Filter、Protocol、Cluster、LoadBalance 等都是通過 SPI 的方式進(jìn)行拓展加載的。

特點:

1、dubbo SPI 為每個拓展點(接口)單獨設(shè)置一個文件,文件名為接口的全限定名。如org.apache.dubbo.rpc.Filter,org.apache.dubbo.rpc.Protocol,org.apache.dubbo.rpc.cluster.LoadBalance 等。

dubbo SPI 配置文件舉例:

2、支持"別名的概念“,可以通過別名獲取拓展點的某個實現(xiàn)。

配置文件內(nèi)容是 key -value 類型,key 是別名,value 是實現(xiàn)類的全限定名。

只使用指定的 filter ,就不會實例化其他 filter 。

3、支持 Dubbo 內(nèi)部的依賴注入

Dubbo IOC

通過 setter 方法進(jìn)行依賴注入。Dubbo 首先會通過反射獲取到實例的所有方法,然后再遍歷方法列表,檢測方法名是否具有 setter 方法特征。若有,則通過 ObjectFactory 獲取依賴對象,最后通過反射調(diào)用 setter 方法將依賴設(shè)置到目標(biāo)對象中。

實現(xiàn):

dubbo SPI 的實現(xiàn)在 ExtensionLoader 這個類。

以獲取所有的 dubbo Filter 為例:

1、首先獲取 Filter 的 ExtensionLoader

ExtensionLoader.getExtensionLoader(Filter.class)

2、由 ExtensionLoader 從配置文件中加載所有的拓展類

加載項目中及 jar 包下以下目錄的配置文件:

配置文件名為接口的全限定名。

3、讀取配置文件時,根據(jù) ’=‘ 為界限,確認(rèn)鍵值對。

由此得到“配置項名稱”到“配置類”的映射關(guān)系表

4、過程中多處使用緩存提升性能。

緩存拓展類對應(yīng)的 ExtensionLoader 等。

獲取到 別名 – 實現(xiàn)類的全限定名后,即可直接通過別名去獲取指定的拓展類。

Java、Spring、Dubbo SPI 對比

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容