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類的例項是不安全的。