原創文章,轉載請標註出處:https://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<>();