1. 程式人生 > >Java spi機制淺談

Java spi機制淺談

最近看到公司的一些框架和之前看到的開源的一些框架的一些服務發現和接入都採用了java的spi機制。

所以簡單的總結下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檔案裡指定實現類的方式來暴露驅動提供者。

3.自己編寫簡單例子

假設有一個內容搜尋系統,分為展示和搜尋兩個模組。展示和搜尋基於介面程式設計。搜尋的實現可能是基於檔案系統的搜尋,也可能是基於資料庫的搜尋。例項程式碼如下

Search.java: 搜尋介面

Java程式碼  收藏程式碼
  1. package search;  
  2. import java.util.List;  
  3. import definition.Doc;  
  4. public interface Search {  
  5.     List<Doc> search(String keyword);  
  6. }  

FileSearch.java:檔案系統的搜尋實現

Java程式碼  收藏程式碼
  1. package search;  
  2. import java.util.List;  
  3. import definition.Doc;  
  4. public class FileSearch implements Search {  
  5.     @Override  
  6.     public List<Doc> search(String keyword) {  
  7.         System.out.println("now use file system search. keyword:" + keyword);  
  8.         return null;  
  9.     }  
  10. }  

DatabaseSearch.java

Java程式碼  收藏程式碼
  1. package search;  
  2. import java.util.List;  
  3. import definition.Doc;  
  4. public class DatabaseSearch implements Search {  
  5.     @Override  
  6.     public List<Doc> search(String keyword) {  
  7.         System.out.println("now use database search. keyword:" + keyword);  
  8.         return null;  
  9.     }  
  10. }  

SearchTest.java

Java程式碼  收藏程式碼
  1. package search;  
  2. import java.util.Iterator;  
  3. import java.util.ServiceLoader;  
  4. public class SearchTest {  
  5.     public static void main(String[] args) {  
  6.         ServiceLoader<Search> s = ServiceLoader.load(Search.class);  
  7.         Iterator<Search> searchs = s.iterator();  
  8.         if (searchs.hasNext()) {  
  9.             Search curSearch = searchs.next();  
  10.             curSearch.search("test");  
  11.         }  
  12.     }  
  13. }  

最後建立在META-INF/searvices/search.Search檔案。

當search.Search檔案內容是"search.FileSearch"時,程式輸出是:

now use file system search. keyword:test

當search.Search檔案內容是"search.DatabaseSearch"時,程式輸出是:

now use database search. keyword:test 
可以看出SearchTest裡沒有任何和具體實現有關的程式碼,而是基於spi的機制去查詢服務的實現。

參考文獻:

(全文完)