Java 中的 SPI 機制是到底是什麼

來自:sigangjun | 責編:樂樂

鏈接:blog.csdn.net/sigangjun/article/details/79071850

 

SPI的全名爲:Service Provider Interface,大多數開發人員可能不熟悉,因爲這個是針對廠商或者插件的。在java.util.ServiceLoader的文檔裏有比較詳細的介紹。 

 

簡單的總結下 Java  SPI 機制的思想。我們系統裏抽象的各個模塊,往往有很多不同的實現方案,比如日誌模塊的方案,xml解析模塊、jdbc模塊的方案等。面向的對象的設計裏,我們一般推薦模塊之間基於接口編程,模塊之間不對實現類進行硬編碼。

 

一旦代碼裏涉及具體的實現類,就違反了可拔插的原則,如果需要替換一種實現,就需要修改代碼。爲了實現在模塊裝配的時候能不在程序裏動態指明,這就需要一種服務發現機制。

 

Java  SPI 就是提供這樣的一個機制:爲某個接口尋找服務實現的機制。有點類似IOC的思想,就是將裝配的控制權移到程序之外,在模塊化設計中這個機制尤其重要。

 

Java  SPI 的具體約定爲:當服務的提供者,提供了服務接口的一種實現之後,在jar包的META-INF/services/目錄裏同時創建一個以服務接口命名的文件。該文件裏就是實現該服務接口的具體實現類。

 

而當外部程序裝配這個模塊的時候,就能通過該jar包META-INF/services/裏的配置文件找到具體的實現類名,並裝載實例化,完成模塊的注入。 

 

基於這樣一個約定就能很好的找到服務接口的實現類,而不需要再代碼裏制定。jdk提供服務實現查找的一個工具類:java.util.ServiceLoader。

 

1.common-logging   

 

apache最早提供的日誌的門面接口。只有接口,沒有實現。具體方案由各提供商實現, 發現日誌提供商是通過掃描 META-INF/services/org.apache.commons.logging.LogFactory配置文件,通過讀取該文件的內容找到日誌提工商實現類。

 

只要我們的日誌實現裏包含了這個文件,並在文件裏制定 LogFactory工廠接口的實現類即可。

 

2.jdbc   

 

 jdbc4.0以前, 開發人員還需要基於Class.forName("xxx")的方式來裝載驅動,jdbc4也基於spi的機制來發現驅動提供商了,可以通過META-INF/services/java.sql.Driver文件裏指定實現類的方式來暴露驅動提供者。

 

一個內容管理系統有一個搜索模塊。是基於接口編程的。搜索的實現可能是基於文件系統的搜索,也可能是基於數據庫的搜索

 

接口定義如下:

 


 
package my.xyz.spi; 
import java.util.List; 
public interface Search { 
  public List serch(String keyword); 
}

 

A公司採用文件系統搜索的方式實現了 Search接口,B公司採用了數據庫系統的方式實現了Search接口。

 

  • A公司實現的類:com.A.spi.impl.FileSearch  

  • B公司實現的類:com.B.spi.impl.DatabaseSearch  

 

那麼A公司發佈 實現jar包時,則要在jar包中META-INF/services/my.xyz.spi.Search文件中寫下如下內容:

 

com.A.spi.impl.FileSearch

 

那麼B公司發佈 實現jar包時,則要在jar包中META-INF/services/my.xyz.spi.Search文件中寫下如下內容:

 

com.B.spi.impl.DatabaseSearch

 

下面是 SPI 測試代碼:

 


 
package com.xyz.factory; 
import java.util.Iterator; 
import java.util.ServiceLoader; 
import my.xyz.spi.Search; 
public class SearchFactory { 
  private SearchFactory() { 
  } 
  public static Search newSearch() { 
    Search search = null; 
    ServiceLoader<Search> serviceLoader = ServiceLoader.load(Search.class); 
    Iterator<Search> searchs = serviceLoader.iterator(); 
    if (searchs.hasNext()) { 
      search = searchs.next(); 
    } 
    return search; 
  } 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章