一、什麼是SPI
SPI的全名爲Service Provider Interface。在java.util.ServiceLoader的文檔裏有比較詳細的介紹。
Java SPI 實際上是“基於接口的編程+策略模式+配置文件”組合實現的動態加載機制。
一個已知的接口或者抽象類,有多個對這個接口或者抽象類的實現。按照SPI的標準在資源路徑META-INF/services目錄下創建一個文件名爲該接口的全限定名。文件裏面一行寫一個對應調用的實現類全限定類名.然後調用該接口裏方法時就可以調用對應的實現類中的實現方法。
二、使用SPI的好處
這種方式主要是針對不同的服務提供廠商,對不同場景的提供不同的解決方案制定的一套標準,舉個簡單的例子,如現在的JDK中有支持音樂播放,假設只支持mp3的播放,有些廠商想在這個基礎之上支持mp4的播放,有的想支持mp5,而這些廠商都是第三方廠商,如果沒有提供SPI這種實現標準,那就只有修改JAVA的源代碼了.
三、SPI使用實例
典型的是Collections.sort(List<T> list,Comparator<? super T> c)這個方法,它的第二個參數是一個實現Comparator接口的實例。我們可以根據自己的排序規則寫一個類,實現此接口,傳入此方法,那麼這個方法就會根據我們的規則對list進行排序。
把這個思想擴展開來,我們用SPI來重新實現上面的例子。客戶把自己的排序規則寫成一個類,並且打包成Jar文件,這個Jar文件裏面必須有META-INF目錄,其下又有services目錄,其下有一個文本文件,文件名即爲接口的全名:java.util.Comparator。
實例文件路徑如下
- src
-main
-resources
- META-INF
- services
- java.util.Comparator -- 文件名(不是文件夾)
文件內容只有一行:(這一行是你實現了Comparator接口的類的全名)
com.company1.ComparatorProvider
Comparator接口實現如下:
package com.company1;
import java.util.Comparator;
import com.mycompany.myapp.MyItem;
public class ComparatorProvider implements Comparator<MyItem>{
@Override
public int compare(MyItem o1, MyItem o2) {
return o1.getName().compareTo(o2.getName()); //依據name排序
}
}
下面是主程序:
//從class path中所有Jar的META-INF目錄中搜索,找到合適的類並加載。
private static ServiceLoader<Comparator> serviceLoader = ServiceLoader.load(Comparator.class);
public static void main(String[] args) {
List<MyItem> myList = new ArrayList<MyItem>();
myList.add(new MyItem(2,"c","hhh"));
myList.add(new MyItem(3,"k","ooo"));
myList.add(new MyItem(4,"d","ppp"));
myList.add(new MyItem(5,"b","ggg"));
showList(myList);
Comparator<MyItem> myCompartor= getCompartor();
Collections.sort(myList,myCompartor);
showList(myList);
}
private static Comparator<MyItem> getCompartor() {
for(Comparator service : serviceLoader) {
return (Comparator<MyItem>)service;
}
return null;
}
要注意的是serviceLoader開始只是加載類,實例化要到第一次用的時候。類MyItem和方法showList並不重要,所以不必在意。你可以按照這個規則,寫另外一個排序規則的Jar,隨時可以更換你的排序規則.