Java基礎系列-SPI你認識嗎

原創文章,轉載請標註出處://www.cnblogs.com/V1haoge/p/10755313.html

一、SPI是什麼

SPI是相對API而言的。

API指的是應用對服務調用方提供的介面,用於提供某種服務、功能,面向的是服務調用方。

SPI指的是應用對服務實現方提供的介面,用於實現某種服務、功能,面向的是服務實現方

二、SPI的使用

2.1 第一步:創建服務介面

package com.dh.spi;

public interface Fruit {
    String getName();
}

2.2 第二步:創建多個服務實現

package com.dh.spi;

public class Apple implements Fruit {
    @Override
    public String getName() {
        return "apple";
    }
}
package com.dh.spi;

public class Banana implements Fruit {
    @Override
    public String getName() {
        return "Banana";
    }
}

這裡的兩個服務實現類,針對的是兩個服務實現方,一方實現了Apple,另一方實現了Banana。

2.3 第三步:創建配置文件

在resource下創建/META-INF/services目錄,在services目錄下創建以服務介面全限定名為名稱的文件:com.dh.spi.Fruit

文件內容為,當前服務實現的服務實現者類的全限定名

com.dh.spi.Apple

2.4 第四步:創建測試類

public class Test {
    public static void main(String[] args) {
        ServiceLoader<Fruit> s = ServiceLoader.load(Fruit.class);
        Iterator<Fruit> it = s.iterator();
        while(it.hasNext())
            System.out.println(it.next().getName());
    }
}

執行結果為:

apple

三、SPI的實現原理

SPI的實現主要依靠的就是ServiceLoader類。使用該類載入介面類型(例如:Fruit.class)

ServiceLoader<Fruit> s = ServiceLoader.load(Fruit.class);

雖然是一個load方法,但是並沒有載入到指定的服務實現類,這裡僅僅是對載入服務實現類做一些準備工作:

  • 創建ServiceLoader
  • 為service賦值
  • 為loader賦值
  • 為acc賦值
  • 清空providers快取
  • 為lookupIterator賦值,其實就是創建一個LazyIterator延遲迭代器。

然後創建迭代器:

Iterator<Fruit> it = s.iterator();

iterator方法中採用了匿名內部類的方式定義了一個新的迭代器,這個迭代器中每一個方法都是通過調用之前創建好的延遲迭代器lookupIterator來完成的

最後就是進行迭代載入了。

while(it.hasNext())
    System.out.println(it.next().getName());

hasNext方法調用了延遲迭代器的hasNext方法,內部調用了hasNextService方法,在這個方法中就會設法去找到指定名稱(META-INF/services/+介面全限定名)的資源文件。並完成讀取文件內容的操作。

然後執行it.next()操作,這個又會調用延遲迭代器的對應方法hasNext,內部調用了nextService方法,這個方法主要功能就是載入上面從文件中讀取到的全限定名稱表示的類。並生成實例,將實例保存到providers中。

private LinkedHashMap<String,S> providers = new LinkedHashMap<>();