參考
清幽之地 https://www.jianshu.com/p/3a3edbcd8f24
一、什么是SPI
SPI ,全稱為 Service Provider Interface,是一種服務發現機制。它通過在ClassPath路徑下的META-INF/services文件夾查找文件,自動加載文件里所定義的類。
這一機制為很多框架擴展提供了可能,比如在Dubbo、JDBC中都使用到了SPI機制。我們先通過一個很簡單的例子來看下它是怎么用的。
二、jdk的SPI
代碼
首先,我們需要定義一個接口,SPIService
package com.viewscenes.netsupervisor.spi; public interface SPIService { void execute(); }
然后,定義兩個實現類,沒別的意思,只輸入一句話。
package com.viewscenes.netsupervisor.spi; public class SpiImpl1 implements SPIService{ public void execute() { System.out.println("SpiImpl1.execute()"); } } ----------------------我是乖巧的分割線---------------------- package com.viewscenes.netsupervisor.spi; public class SpiImpl2 implements SPIService{ public void execute() { System.out.println("SpiImpl2.execute()"); } }
最后呢,要在ClassPath路徑下配置添加一個文件。文件名字是接口的全限定類名,內容是實現類的全限定類名,多個實現類用換行符分隔。
文件路徑如下:

SPI配置文件位置
內容就是實現類的全限定類名:
com.viewscenes.netsupervisor.spi.SpiImpl1
com.viewscenes.netsupervisor.spi.SpiImpl2
測試
然后我們就可以通過ServiceLoader.load或者Service.providers
方法拿到實現類的實例。其中,Service.providers
包位於sun.misc.Service
,而ServiceLoader.load
包位於java.util.ServiceLoader
。
public class Test { public static void main(String[] args) { Iterator<SPIService> providers = Service.providers(SPIService.class); ServiceLoader<SPIService> load = ServiceLoader.load(SPIService.class); while(providers.hasNext()) { SPIService ser = providers.next(); ser.execute(); } System.out.println("--------------------------------"); Iterator<SPIService> iterator = load.iterator(); while(iterator.hasNext()) { SPIService ser = iterator.next(); ser.execute(); } } }
兩種方式的輸出結果是一致的:
SpiImpl1.execute() SpiImpl2.execute() -------------------------------- SpiImpl1.execute() SpiImpl2.execute()
優缺點
優點:將業務代碼和具體實現類解耦,方便擴展。如需增加新邏輯,無需修改主流程,直接在PI配置文件增加實現類的全限定名即可。
缺點:顆粒度不夠細,無法准確定位某一個實現類。要執行就執行所有的實現類。
三、Dubbo 的 SPI(對jdk SPI的顆粒度改進)
Dubbo的配置文件
cluster這里可以准確制定一個實現類
SPI配置文件
和jdk只包含實現類的全限定名不同,這里是key value 模式