Java SPI 與 Dubbo SPI 有什么區(qū)別?
點擊上方藍色“小哈學(xué)Java”,選擇“設(shè)為星標(biāo)” 回復(fù)“資源”獲取獨家整理的學(xué)習(xí)資料!
作者:廢物大師兄
來源:www.cnblogs.com/cjsblog/p/14346766.html
SPI(Service Provider Interface)是JDK內(nèi)置的一種服務(wù)提供發(fā)現(xiàn)機制。本質(zhì)是將接口實現(xiàn)類的全限定名配置在文件中,并由服務(wù)加載器讀取配置文件,加載實現(xiàn)類。這樣可以在運行時,動態(tài)為接口替換實現(xiàn)類。
在Java中SPI是被用來設(shè)計給服務(wù)提供商做插件使用的。基于策略模式來實現(xiàn)動態(tài)加載的機制。我們在程序只定義一個接口,具體的實現(xiàn)交個不同的服務(wù)提供者;在程序啟動的時候,讀取配置文件,由配置確定要調(diào)用哪一個實現(xiàn)。有很多組件的實現(xiàn),如日志、數(shù)據(jù)庫訪問等都是采用這樣的方式,最常用的就是 JDBC 驅(qū)動。
1、Java SPI
核心類:java.util.ServiceLoader

服務(wù)是一組眾所周知的接口和(通常是抽象的)類。服務(wù)提供者是服務(wù)的特定實現(xiàn)。提供者中的類通常實現(xiàn)接口,并子類化服務(wù)本身中定義的類。服務(wù)提供者可以以擴展的形式安裝在Java平臺的實現(xiàn)中,即放置在任何常見擴展目錄中的jar文件。提供程序也可以通過將它們添加到應(yīng)用程序的類路徑或其他特定于平臺的方法來提供。
通過在資源目錄META-INF/services中放置一個提供程序配置文件來識別服務(wù)提供程序。文件名是服務(wù)類型的完全限定二進制名稱。該文件包含具體提供程序類的完全限定二進制名的列表,每行一個。每個名稱周圍的空格和制表符以及空白行將被忽略。注釋字符是'#';在每一行中,第一個注釋字符之后的所有字符都將被忽略。文件必須用UTF-8編碼。
按照上面的方法,我們來寫個例子試一下
首先,定義一個接口Car
package org.example;
public interface Car {
void run();
}
兩個實現(xiàn)類
package org.example;
public class ToyotaCar implements Car {
@Override
public void run() {
System.out.println("Toyota");
}
}
package org.example;
public class HondaCar implements Car {
@Override
public void run() {
System.out.println("Honda");
}
}
在META-INF/services下創(chuàng)建一個名為org.example.Car的文本文件
org.example.ToyotaCar
org.example.HondaCar
最后,寫個測試類運行看一下效果
package org.example;
import java.util.ServiceLoader;
public class App
{
public static void main( String[] args )
{
ServiceLoader<Car> serviceLoader = ServiceLoader.load(Car.class);
serviceLoader.forEach(x->x.run());
}
}

跟一下ServiceLoader的代碼,看看是怎么找到服務(wù)實現(xiàn)的

用當(dāng)前線程的類加載器加載

接口和類加載器都有了,萬事俱備只欠東風(fēng)



Java SPI 不足之處:
不能按需加載。Java SPI在加載擴展點的時候,會一次性加載所有可用的擴展點,很多是不需要的,會浪費系統(tǒng)資源 獲取某個實現(xiàn)類的方式不夠靈活,只能通過 Iterator 形式獲取,不能根據(jù)某個參數(shù)來獲取對應(yīng)的實現(xiàn)類 不支持AOP與IOC 如果擴展點加載失敗,會導(dǎo)致調(diào)用方報錯,導(dǎo)致追蹤問題很困難
2、Dubbo SPI
Dubbo重新實現(xiàn)了一套功能更強的SPI機制, 支持了AOP與依賴注入,并且利用緩存提高加載實現(xiàn)類的性能,同時支持實現(xiàn)類的靈活獲取。
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.8</version>
</dependency>
核心類:org.apache.dubbo.common.extension.ExtensionLoader
先來了解一下@SPI注解,@SPI是用來標(biāo)記接口是一個可擴展的接口
改造一下前面的例子,在Car接口上加上@SPI注解
package org.example;
import org.apache.dubbo.common.extension.SPI;
@SPI
public interface Car {
void run();
}
兩個實現(xiàn)類不變。
在META-INF/dubbo目錄下創(chuàng)建名為org.example.Car的文本文件,內(nèi)容如下(鍵值對形式):
toyota=org.example.ToyotaCar
honda=org.example.HondaCar
package org.example;
import org.apache.dubbo.common.extension.ExtensionLoader;
import java.util.ServiceLoader;
public class App
{
public static void main( String[] args )
{
// Java SPI
ServiceLoader<Car> serviceLoader = ServiceLoader.load(Car.class);
serviceLoader.forEach(x->x.run());
// Dubbo SPI
ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
Car car = extensionLoader.getExtension("honda");
car.run();
}
}


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






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



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




回到之前策略那個地方,將策略按順序排列,依次遍歷所有的策略來加載。就是在那三個目錄下查找指定的文件,并讀取其中的內(nèi)容

跟之前的ServiceLoader如出一轍

遇到@Adaptive標(biāo)注的就緩存起來

最近面試BAT,整理一份面試資料《Java面試BATJ通關(guān)手冊》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。
獲取方式:點“在看”,關(guān)注公眾號并回復(fù) Java 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。
文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。
謝謝支持喲 (*^__^*)


