Java SPI 機制

一、什麼是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,隨時可以更換你的排序規則.

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