1. 程式人生 > 實用技巧 >在java中使用SPI建立可擴充套件的應用程式打了款水dfjkljw

在java中使用SPI建立可擴充套件的應用程式打了款水dfjkljw

什麼是可擴充套件的應用程式呢?可擴充套件的意思是不需要修改原始程式碼,就可以擴充套件應用程式的功能。我們將應用程式做成外掛或者模組。

這樣可以在不修改原應用的基礎上,對系統功能進行升級或者定製化。

本文將會向大家介紹如何通過java中的SPI機制實現這種可擴充套件的應用程式。

SPI簡介
SPI的全稱是Java Service Provider Interface。是java提供的一種服務發現的機制。

通過遵循相應的規則編寫應用程式之後,就可以使用ServiceLoader來載入相應的服務了。

SPI的實現主要分為4個部分:

Service Provider Interface: SPI是一個interface或者是抽象類,其中定義了我們需要擴充套件實現的功能。
Service Providers:這是SPI的具體實現,提供了具體的實現功能
SPI Configuration File:SPI的配置檔案,通過在配置檔案我們來配置相關的SPI發現資訊。
ServiceLoader: ServiceLoader是用來載入和發現服務的java類,並提供了很多有用的方法。
SPI的普通java實現
講完SPI的定義,大家可能還是不清楚SPI到底是做什麼的,又該怎麼使用它。

不用急,我們下面通過一個例子來說明。

首先建立一個module:SPI-service,裡面主要定義了一個ModuleService介面:

public interface ModuleService {
然後再分別建立兩個module,作為ModuleService的實現:

public class ModuleServiceA implements ModuleService {

public ModuleService getModuleService(){
    return new ModuleServiceA();
}

}
public class ModuleServiceB implements ModuleService {

public ModuleService getModuleService(){
    return new ModuleServiceB();
}

}
接著分別在兩個module中建立META-INF/services資料夾,並且在裡面建立兩個以 Service Provider Interface限定名為名字的檔案,這裡檔名是:com.flydean.base.service.ModuleService,檔案裡面存放的是SPI的具體實現類:
com.flydean.base.servicea.ModuleServiceA
com.flydean.base.serviceb.ModuleServiceB
最後,我們需要建立一個使用SPI的類:
public class ModuleController {

public static void main(String[] args) {
    List<ModuleService> moduleServices = ServiceLoader
            .load(ModuleService.class).stream()
            .map(ServiceLoader.Provider::get)
            .collect(toList());
    log.info("{}", moduleServices);
}

}
為了更好的展示擴充套件應用的實際使用,我們分別建立4個模組。在實際應用中,只需要將這些jar包加入應用程式的classpath即可。

執行看下輸出結果:

[com.flydean.base.servicea.ModuleServiceA@16f65612,
com.flydean.base.serviceb.ModuleServiceB@311d617d]
從結果看到,我們獲得了兩個ModuleService。證明系統擴充套件成功。

SPI在JPMS模組化系統下的實現
上面我們講的是基本的操作,考慮一下,如果是在JDK9之後,引入了JPMS模組化系統之後,應該怎麼使用SPI呢?

程式碼肯定是一樣,我們需要修改的是SPI配置檔案。

如果在JPMS中,我們就不需要使用META-INF/services了,我們只需要建立相應的module-info.java檔案即可。

先看下SPI模組的module-info.java檔案:

module com.flydean.service {
exports com.flydean.service;
這個模組我們對外暴露了service package,供其他模組呼叫。
接下來是SPI的實現模組:
module com.flydean.servicea {
requires com.flydean.service;
provides com.flydean.service.ModuleService with com.flydean.servicea.ModuleServiceA;
exports com.flydean.servicea;
}
這裡我們使用了provides命令,定義了兩個類的關聯關係。
最後是呼叫的模組:
module com.flydean.controller {
uses com.flydean.service.ModuleService;
requires com.flydean.service;
requires lombok;
requires slf4j.api;
}
這裡我們使用uses關鍵詞來引用ModuleService