SPI 全稱為 (Service Provider Interface) ,是JDK內建的一種服務提供發現機制。
為什麼需要SPI?
我們的現代系統越來越龐大,如果設計架構有問題,就可能牽一髮而動全身,在物件導向中我們推薦基於介面程式設計,模組之間基於介面程式設計,這樣的好處顯而易見,不在程式碼中進行硬編碼,不同的實現者按照介面規範實現自己內部操作,然後在使用的時候再根據 SPI 的規範去獲取對應的服務提供者的服務實現。通過 SPI 服務載入機制進行服務的註冊和發現,可以有效的避免在程式碼中將服務提供者寫死。從而可以基於介面程式設計,實現模組間的解耦。
SPI機制約定:
1.在 META-INF/services/ 目錄中建立以介面全限定名命名的檔案,該檔案內容為API具體實 現類的全限定名
2.使用 ServiceLoader 類動態載入 META-INF 中的實現類
3.如 SPI 的實現類為 Jar 則需要放在主程式 ClassPath 中
4.API 具體實現類必須有一個不帶引數的構造方法
如圖:
現在已經被使用的案例:
- common-logging Apache最早提供的日誌的門面介面。只有介面,沒有實現。具體方 案由各提供商實現, 發現日誌提供商是通過掃描 META- INF/services/org.apache.commons.logging.LogFactory配置檔案,通過讀取該文 件的內容 找到日誌提工商實現類。只要我們的日誌實現裡包含了這個檔案,並在檔案裡 制定LogFactory工廠介面的實現類即可。
- JDBC jdbc4.0以前, 開發人員還需要基於Class.forName("xxx")的方式來裝載驅動。 建立連線: DriverManage.getConnection()中,有Connection con = aDriver.driver.connect(url, info); driver成員變數,是java.sql.Driver介面,Java對外公開的一個載入驅動介面,Java並未實現,至於實現這個介面由各個Jdbc廠商去實現。 如MySQL,mysql-connector-java-5.1.38.jar包下面META-INF.services包下有個java.sql.Driver檔案開啟檔案有下面兩行 com.mysql.jdbc.Driver com.mysql.fabric.jdbc.FabricMySQLDriver
示例Demo:
- 建立maven專案
- 目錄如下:
程式碼
OrderService.javapackage com.demo.spi.service; public interface OrderService { int getOrderCountById(int id); } 複製程式碼
CustomerOrderServiceImpl.java
package com.demo.spi.impl;
import com.demo.spi.service.OrderService;
public class CustomerOrderServiceImpl implements OrderService {
public int getOrderCountById(int id) {
System.out.println("cutomer order count is 10");
return 10;
}
}
複製程式碼
AgencyOrderServiceImpl.java
package com.demo.spi.impl;
import com.demo.spi.service.OrderService;
public class AgencyOrderServiceImpl implements OrderService {
public int getOrderCountById(int id) {
System.out.println("agency order count is 20");
return 20;
}
}
複製程式碼
META-INF下檔名:com.demo.spi.service.OrderService,檔案內容:
com.demo.spi.impl.AgencyOrderServiceImpl
com.demo.spi.impl.CustomerOrderServiceImpl複製程式碼
4.新建測試專案java project
程式碼github地址:https://github.com/HuoMoreMore/demo-spi
執行main方法之前我們需要將第一個專案進行打包 jar 依賴到第二個java 專案中來,完成之後點選run,可以看到列印出了,我們在專案1 中的兩個serviceImpl方法的輸出,也就是說ServiceLoader 動態的通過jar中的配置找到了 專案1中的實現類並且把他記載到了記憶體中,我們就可以直接呼叫專案1中提供的兩個實現類,並且正確輸出。
如果有需要了解ServiceLoader原始碼的朋友可以參考:
https://www.jianshu.com/p/a6073e9f8cb4