JavaSPI詳解

目錄

  • 一個問題
  • 什么是SPI
  • API 與 SPI
  • 一個簡單的例子
  • SPI機制的實現
  • Java SPI的問題
  • 為什么SPI機制打破了雙親委派模型
  • 參考資料
一個問題在項目開發中,經常會使用到數據庫驅動,我們連接的數據庫可能是MySQL也有可能是Oracle,但是不管使用什么數據庫都是引入數據庫驅動配置相應的地址、用戶、密碼信息就可以使用而不用修改業務代碼 。
這是因為在JDK中提供了一個java.sql.Driver接口 。各個數據庫廠商只需要實現這個接口,當我們引入相應驅動,連接數據庫的時候,就會使用廠商提供的實現,那么又是如何知道廠商實現的類路徑的呢??
什么是SPISPI全名Service Provider interface,翻譯過來就是“服務提供接口”,再說簡單就是提供某一個服務的接口,提供給服務開發者或者服務生產商來進行實現 。Java SPIJDK內置的一種動態加載擴展點的實現 。這個機制在一般的業務代碼中很少用到(個人接觸到的業務沒有用到過),但是再底層框架中卻被大量使用,包括JDBCDubboSpring框架、日志接口中都有用到,不同的是有的使用Java原生的實現,有的框架則自己實現了一套SPI機制
API 與 SPIAPI 全稱Application Programming Interface,翻譯為“應用程序接口”,指的是應用程序為外部提供服務的接口,這個接口通常由服務提供者自行開發,定義好接口后很少改動 。APISPI示意圖如圖1,圖2所示
JavaSPI詳解

文章插圖
圖1  API示意圖
JavaSPI詳解

文章插圖
圖2  SPI示意圖一般應用(模塊)之間通過接口進行通訊,服務提供方提供接口并進行實現后,調用方就可以通過調用這個接口擁有服務提供發提供的能力,這個就是API當接口是由服務調用方提供,并且由服務提供方進行實現時,服務調用方就可以根據自己的需要選擇特定實現,而不用更改業務代碼以獲取相應的功能,這個就是SPI
一個簡單的例子這個功能是向注冊中心注冊服務的一個示例(過于簡單的示例),
首先定義一個接口Registry, 這個接口只有一個功能,就是向注冊中心注冊一個服務,但是我現在并不確定我選的是什么注冊中心,于是提供了一個統一的接口,由各個廠商進行實現
package cn.bmilk.chat.spi;public interface Registry {    void  registry(String host, int port);}廠商實現好后,需要在其META-INF/services文件夾下新增一個文件,文件名為該接口的全限定名即:cn.bmilk.chat.spi.Registry, 內容為接口實現的全限定名,這里我寫了兩個
cn.bmilk.chat.spi.EurekaRegistrycn.bmilk.chat.spi.ZookeeperRegistry兩個類的都是空實現,內容如下
    @Override    public void registry(String host, int port) {        System.out.println(this + "registry , host = " + host +"  port = " + port);    }下面編寫測試主類,通過 ServiceLoader 加載 Registry 實現
public class MainTest {    public static void main(String[] args) {        ServiceLoader<Registry> load = ServiceLoader.load(Registry.class);        Iterator<Registry> iterator = load.iterator();        while (iterator.hasNext()){            Registry registry = iterator.next();            registry.registry("127.0.0.1", 10086);        }    }}

經驗總結擴展閱讀