【SPI】 --- java spi 機制簡介

本文對應源碼地址:https://github.com/nieandsun/dubbo-study


1 spi需要解決的問題剖析

首選看如下代碼:

@Test
public void demo1() {
    JdbcService jdbcService = new JdbcServiceAImpl();
    int i = jdbcService.insert("james");
    if (i == 1) {
        System.out.println("插入成功");
    }
}

相信上面的代碼對於只要有過java開發經驗的人員來說,肯定都非常熟悉。

但是設想這樣一個場景,假設如果此時只有JdbcService接口,沒有其具體實現,那我們能不能就非要拿到一個實現類(或者說一個符號),將上面的程序完成,等到你真有實現類了,我就直接去某個地方找到你的實現類,來調用你的方法呢?

其實這就是SPI所要解決的問題。


SPI 全稱 Service Provider Interface, 是 Java 提供的一套用來被第三方實現或
者擴展的 API, 它可以用來啓用框架擴展和替換組件。

在這裏插入圖片描述
可以看到, SPI 的本質, 其實是幫助程序, 爲某個特定的接口尋找它的實現類。 而且哪些實現類的會加載, 是個動態過程(不是提前預定好的)。

有點類似 IOC 的思想, 就是將裝配的控制權移到程序之外, 在模塊化設計中這個機制尤其重要。 所以 SPI 的核心思想就是解耦。

比較常見的例子:

  • 數據庫驅動加載接口實現類的加載
    JDBC 加載不同類型數據庫的驅動
  • 日 志門面接口實現類加載
    SLF4J 加載不同提供商的日 志實現類
  • Spring
    Spring 中大量使用了 SPI, 比如: 對 servlet3. 0 規範對
    ServletContainerInitializer 的實現、 自 動類型轉換 Type Conversion
    SPI(Converter SPI、 Formatter SPI) 等

2 java spi使用簡介


2.1 使用java spi需要遵循的約定

要使用 Java SPI, 需要遵循如下約定:

  • (1) 當服務提供者提供了接口的一種具體實現後, 在 jar 包的META-INF/services目 錄下創建一個以“接口全限定名”爲命名的文件,內容爲實現類的全限定名
  • (2) 接口實現類所在的 jar 包放在主程序的 classpath 中;
  • (3) 主程序通過 java. util. ServiceLoder 動態裝載實現模塊, 它通過掃描META-INF/services 目 錄下的配置文件找到實現類的全限定名, 把類加載到 JVM;
  • (4)SPI 的實現類必須攜帶一個不帶參數的構造方法;

2.2 示例

(1)先定義一個接口

public interface JdbcService {
    int insert(String name);
}

(2)再定義一系列它的具體實現

  • 第一個實現
public class JdbcServiceAImpl implements JdbcService {
    @Override
    public int insert(String name) {
        System.out.println(name + ",你好,調通了A實現!");
        return 1;
    }
}
  • 第二個實現
public class JdbcServiceBImpl implements JdbcService {
    @Override
    public int insert(String name) {
        System.out.println(name + ",你好,調通了B實現!");
        return 1;
    }
}
  • 第三個實現
public class JdbcServiceCImpl implements JdbcService {
    @Override
    public int insert(String name) {
        System.out.println(name + ",你好,調通了C實現!");
        return 1;
    }
}

等等


(3) 在 源碼的META-INF/services目錄下創建一個以“接口全限定名”爲命名的文件,內容爲實現類的全限定名
在這裏插入圖片描述

  • 測試類
/**
 * java spi機制驗證
 */
@Test
public void javaSPI() {
    //服務加載器,加載實現類
    ServiceLoader<JdbcService> serviceLoader = ServiceLoader.load(JdbcService.class);

    //serviceLoader是實現了Iterable的迭代器,直接遍歷實現類
    for (JdbcService service : serviceLoader) {
        int james = service.insert("james");//依次調用實現類
        System.out.println(james);
    }
}

可以看到本測試類與本文剛開始時的代碼最明顯的不同就是,這裏並沒有出現任何接口的具體實現類。

也就是說ServiceLoader可以根據下面三個信息,生成具體的接口實現類。

  • (1)接口的類型 — 這裏是JdbcService.class
  • (2)在META-INF/services下以接口的全限定名爲名稱的文件
  • (3)以及文件裏的實現類的全限定名

簡單看一下測試結果:
在這裏插入圖片描述


這篇文章讓我想起了《【springboot】— springboot的starter原理探究 + 如何自定義自己的starter》!!!


end!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章