乐趣区

关于java:在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 接口:

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。

总结

本文介绍了 SPI 在模块化和非模块化零碎中的利用。

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

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

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!

退出移动版