一、什麼是SPI
SPI ,全稱爲 Service Provider Interface,是一種服務發現機制。JDK中的SPI是通過在ClassPath路徑下的META-INF/services文件夾查找擴展文件,自動加載文件裏所定義的類。
在小編的理解來,覺得它更是一種思想。即找到服務的接口, 美其名曰: 服務發現機制思想。很多開源框架都有借用這種思想,比如dubbo、jdbc。
二、SPI在JDK中如何使用
SPI在JDK中,我們可以使用 ServiceLoader
類進行使用。
1. 前提準備
public interface SpiService {
String say();
}
兩個實現類
public class ASpiServiceImpl implements SpiService {
static {
System.out.println("static init a");
}
{
System.out.println("init a");
}
@Override
public String say() {
return "A";
}
}
public class BSpiServiceImpl implements SpiService {
static {
System.out.println("static init b");
}
{
System.out.println("init b");
}
@Override
public String say() {
return "B";
}
}
2. 進行配置
在resources中創建META-INF/services目錄
│ └── resources
│ └── META-INF
│ └── services
│ └── com.github.easylog.spi.SpiService
com.github.easylog.spi.SpiService文件內容
com.github.easylog.spi.impl.ASpiServiceImpl
com.github.easylog.spi.impl.BSpiServiceImpl
3. 使用
通過ServiceLoader類我們可以加載到所有配置的實現類,並對實現類進行處理。需要注意一點的是,看4使用注意。
public class SpiTester {
public static void main(String[] args) {
ServiceLoader<SpiService> spiServices = ServiceLoader.load(SpiService.class);
Iterator<SpiService> iterator = spiServices.iterator();
while (iterator.hasNext()) {
SpiService next = iterator.next();
System.out.println(next.say());
}
}
}
4. 使用注意
可以看下小編前面聲明的兩個實現類,都定義了靜態代碼塊和非靜態代碼塊。正常情況當這個字節碼被加載,就會執行靜態代碼塊裏面的內容,但是實際運行時候卻沒有執行, 其實是有原因的。
可以看到第二個參數是false。即加載時候不進行初始化。
三、Dubbo中服務發現思想
服務發現這種思想的特點是: 代碼不是硬編碼的方式,而是可配置的。只要將要支持的實現類放到指定配置文件下面,就會自動被加載起來了。然後代碼中只關心使用即可。我們可以利用這種思想來實現, 框架的擴展,比如前面說了。Dubbo會利用SPI的思想進行,加載用戶自定義的過濾器。
這種思想特別適合做服務擴展。現在大多數開源框架中都會使用到這種思想。
1. 定義過濾器
@Activate(group = { Constants.PROVIDER })
public class ProviderHelloFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
System.out.pringln("hello ok!");
return invoker.invoke(invocation);
}
}
2. 添加配置文件
META-INF/dubbo/Interal/com.alibaba.dubbo.rpc.Filter
默認支持的過濾器
利用SPI原理,我們自定義一個過濾器
3. 使用
其實API跟JDK中使用ServiceLoader的方式,非常類同。唯一不同的是Dubbo中是使用ExtensionLoader。因爲dubbo中做了一些特殊的增強處理。比如在配置文件中支持自定義一個別名key。如上圖hello就是key。通過getExtension(“hello”)就能獲取指定的實現類。
public class SpiTester {
public static void main(String[] args) throws Exception{
ExtensionLoader<Filter> filterExtensionLoader = ExtensionLoader.getExtensionLoader(Filter.class);
Set<String> supportedExtensions = filterExtensionLoader.getSupportedExtensions();
System.out.println(supportedExtensions);
//[accesslog, activelimit, cache...]
Filter hello = filterExtensionLoader.getExtension("hello");
//com.github.easylog.spi.ProviderHelloFilter@299a06ac
System.out.println(hello);
}
}
**那麼這種思想你學會了嗎? **
最後求關注,求訂閱,謝謝你的閱讀!