首頁>技術>

JAVA的SPI機制是什麼?

SPI(Service Provider Interface): ,“服務提供者介面”,是指在 服務使用方 角度提出的“介面要求”,是對“服務提供方”提出的約定,簡單說就是:“我需要這樣的服務,現在你們來滿足”。

是不是看起來很高深的樣子.其實特別簡單 我們先來一個例子,大家更容易理解什麼是JAVA的SPI機制:

首先我們提供了一個訂單處理的介面

/** * @Author: lty * @Date: 2021/1/22 14:24 * 訂單的處理介面 */public interface OrderHandlerService {    String  handler(String orderid);}

兩個實現類

public class PddOrderHandler implements OrderHandlerService{    @Override    public String handler(String orderid) {        System.out.println("Pdd handler execute");        return "Pdd handler execute";    }}public class TaobaoOrderHandler implements OrderHandlerService{    @Override    public String handler(String orderid) {        System.out.println("taobao handler execute");        return "taobao handler execute";    }}

類圖關係:

1. 透過直接呼叫實現類的方式

平常在開發過程中 我們例項一個物件是透過new 的方式

public static void main(String[] args) {        OrderHandlerService pddOrderHandler = new PddOrderHandler();        pddOrderHandler.handler("000000001");}        //Pdd handler execute
2. 透過SPI 提供實現類的方式

透過ServiceLoader.load()方法獲取實現類

public static void main(String[] args) {        //使用spi        ServiceLoader<OrderHandlerService> services = ServiceLoader.load(OrderHandlerService.class);        services.forEach(orderHandlerService -> {            orderHandlerService.handler("000001");        });                //Pdd handler execute        //taobao handler execute }

注意:

透過SPI方式 我們需要提供一個特別的檔案:

檔案位於 /resources/META-INF/services

檔名為 com.xxx. 即介面的全限定名稱。

內容為兩個實現類的全限定名稱:

com.liangtengyu.service.Impl.PddOrderHandlercom.liangtengyu.service.Impl.TaobaoOrderHandler

具體的底層實現可以分離出來 實現外部載入,也可以將每組實現和SPI配置檔案打包成不同的jar,在具體使用時根據需要使用不同的jar即可。

在原始碼中

ServiceLoader類定義了一個字首 private static final String PREFIX = "META-INF/services/"

用來約定上述指定的位置,基於約定的配置讀取會從這裡查詢,如果我們引入了第三方的jar包,如果jar中的META-INF/service有OrderHandlerService的實現,也會被讀取,並且例項化裡面的類。

SPI的應用

這裡我們以JDBC為例子

mysql-connector-java:5.1.32 包的 META-INF/services/ 目錄下有個 java.sql.Driver

檔案,內容為:

com.mysql.jdbc.Drivercom.mysql.fabric.jdbc.FabricMySQLDriver

在SqlLite中 也有同樣的檔案.

它們都是用來載入實現了java.sql.Driver介面實現類的位置

//內容為org.sqlite.JDBC

其它的應用:

日誌門面介面實現類載入,SLF4J載入不同提供商的日誌實現類

Spring中大量使用了SPI,比如:對servlet3.0規範對ServletContainerInitializer的實現、自動型別轉換Type Conversion SPI(Converter SPI、Formatter SPI)等

Dubbo中也大量使用SPI的方式實現框架的擴充套件, 不過它對Java提供的原生SPI做了封裝,允許使用者擴充套件實現Filter介面

...

總結

優點: 使用Java SPI機制的優勢是實現解耦,使得第三方服務模組的裝配控制的邏輯與呼叫者的業務程式碼分離,而不是耦合在一起。應用程式可以根據實際業務情況啟用框架擴充套件或替換框架元件。

缺點:雖然ServiceLoader也算是使用的延遲載入,但是基本只能透過遍歷全部獲取,也就是介面的實現類全部載入並例項化一遍。如果你並不想用某些實現類,它也被載入並例項化了,這就造成了浪費。獲取某個實現類,的方式不夠靈活,多個併發多執行緒使用ServiceLoader類的例項是不安全的。

5
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 大神把視覺化放進資料地圖裡,原來不敲程式碼一樣能做