在java中使用SPI創建可擴展的應用程序

簡介

什麼是可擴展的應用程序呢?可擴展的意思是不需要修改原始代碼,就可以擴展應用程序的功能。我們將應用程序做成插件或者模塊。

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

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

SPI簡介

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

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

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

  1. Service Provider Interface: SPI是一個interface或者是抽象類,其中定義了我們需要擴展實現的功能。
  2. Service Providers:這是SPI的具體實現,提供了具體的實現功能
  3. SPI Configuration File:SPI的配置文件,通過在配置文件我們來配置相關的SPI發現信息。
  4. ServiceLoader: ServiceLoader是用來加載和發現服務的java類,並提供了很多有用的方法。

SPI的普通java實現

講完SPI的定義,大家可能還是不清楚SPI到底是做什麼的,又該怎麼使用它。

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

首先創建一個module:SPI-service,裡面主要定義了一個ModuleService接口:

<code>public interface ModuleService {
}
/<code>
在java中使用SPI創建可擴展的應用程序

然後再分別創建兩個module,作為ModuleService的實現:

<code>public class ModuleServiceA implements ModuleService {

    public ModuleService getModuleService(){
        return new ModuleServiceA();
    }
}
/<code>
<code>public class ModuleServiceB implements ModuleService {

    public ModuleService getModuleService(){
        return new ModuleServiceB();
    }
}
/<code>

接著分別在兩個module中創建META-INF/services文件夾,並且在裡面創建兩個以 Service Provider Interface限定名為名字的文件,這裡文件名是:com.flydean.base.service.ModuleService,文件裡面存放的是SPI的具體實現類:

<code>com.flydean.base.servicea.ModuleServiceA
com.flydean.base.serviceb.ModuleServiceB
/<code>
在java中使用SPI創建可擴展的應用程序

在java中使用SPI創建可擴展的應用程序

最後,我們需要創建一個使用SPI的類:

<code>public class ModuleController {

    public static void main(String[] args) {
        List moduleServices = ServiceLoader
                .load(ModuleService.class).stream()
                .map(ServiceLoader.Provider::get)
                .collect(toList());
        log.info("{}", moduleServices);
    }
}
/<code>
在java中使用SPI創建可擴展的應用程序

為了更好的展示擴展應用的實際使用,我們分別創建4個模塊。在實際應用中,只需要將這些jar包加入應用程序的classpath即可。

運行看下輸出結果:

<code>[com.flydean.base.servicea.ModuleServiceA@16f65612, 
com.flydean.base.serviceb.ModuleServiceB@311d617d]
/<code>

從結果看到,我們獲得了兩個ModuleService。證明系統擴展成功。

SPI在JPMS模塊化系統下的實現

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

代碼肯定是一樣,我們需要修改的是SPI配置文件。

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

先看下SPI模塊的module-info.java文件:

<code>module com.flydean.service {
    exports com.flydean.service;
}
/<code>

這個模塊我們對外暴露了service package,供其他模塊調用。

在java中使用SPI創建可擴展的應用程序

接下來是SPI的實現模塊:

<code>module com.flydean.servicea {
    requires com.flydean.service;
    provides com.flydean.service.ModuleService with com.flydean.servicea.ModuleServiceA;
    exports com.flydean.servicea;
}
/<code>

這裡我們使用了provides命令,定義了兩個類的關聯關係。

在java中使用SPI創建可擴展的應用程序

最後是調用的模塊:

<code>module com.flydean.controller {
    uses com.flydean.service.ModuleService;
    requires com.flydean.service;
    requires lombok;
    requires slf4j.api;
}
/<code>

這裡我們使用uses關鍵詞來引用ModuleService。

在java中使用SPI創建可擴展的應用程序

總結

本文介紹了SPI在模塊化和非模塊化系統中的應用。

本文中的例子:learn-java-base-9-to-20

本文已收錄於 http://www.flydean.com/java-spi-for-extensible-app/

最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

歡迎關注我的公眾號:「程序那些事」,懂技術,更懂你!


分享到:


相關文章: