一、SPI机制介绍
SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件。 SPI的作用就是为这些被扩展的API寻找服务实现。
1.1 SPI和API的区别
API (Application Programming Interface)在大多数情况下,都是实现方制定接口并完成对接口的实现,调用方仅仅依赖接口调用,且无权选择不同实现。 从使用人员上来说,API 直接被应用开发人员使用。如下图所示,其中模块A为接口制定方和实现方,而模块B为接口的使用放。
维基百科关于API的描述:In building applications, an API (application programming interface) simplifies programming by abstracting the underlying implementation and only exposing objects or actions the developer needs. While a graphical interface for an email client might provide a user with a button that performs all the steps for fetching and highlighting new emails, an API for file input/output might give the developer a function that copies a file from one location to another without requiring that the developer understand the file system operations occurring behind the scenes.
SPI (Service Provider Interface)是调用方来制定接口规范,提供给外部来实现,调用方在调用时则选择自己需要的外部实现,可用于启用框架扩展和可替换组件。 从使用人员上来说,SPI 被框架扩展人员使用。如下图所示,A模块是接口的定义方和使用方,而B模块则是接口实现方。
SPI 维基百科定义:Service Provider Interface (SPI) is an API intended to be implemented or extended by a third party. It can be used to enable framework extension and replaceable components
SPI java官方定义:A service is a well-known set of interfaces and (usually abstract) classes. A service provider(SPI) is a specific implementation of a service. The classes in a provider typically implement the interfaces and subclass the classes defined in the service itself. Service providers can be installed in an implementation of the Java platform in the form of extensions, that is, jar files placed into any of the usual extension directories. Providers can also be made available by adding them to the application’s class path or by some other platform-specific means.
1.2 SPI的优点
使用 Java SPI 机制的优势是实现解耦,使得接口的定义与具体业务实现分离,而不是耦合在一起。应用进程可以根据实际业务情况启用或替换具体组件。以java中的JDBC数据库驱动为例,java官方在核心库制定了java.sql.Driver数据库驱动接口,使用该接口实现了数据库链接等逻辑,但是并没有具体实现数据库驱动接口,而是交给MySql等厂商去实现具体的数据库接口。
引用自博客:Java SPI 主要是应用于厂商自定义组件或插件中。在java.util.ServiceLoader的文档里有比较详细的介绍。简单的总结下java SPI机制的思想:我们系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块、xml解析模块、jdbc模块等方案。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。 Java SPI就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。
二、SPI的约定
服务提供方需要通过一些约定告诉系统自己所提供的服务的位置,java9之后一共有两种约定方式:
- 通过在META-INF/services/ 目录下配置相关文件实现。
- 通过java9 jigsaw的导出语句指定服务位置。
A service provider is a single type, usually a concrete class. An interface or abstract class is permitted because it may declare a static provider method, discussed later. The type must be public and must not be an inner class.
A service provider and its supporting code may be developed in a module, which is then deployed on the application module path or in a modular image. Alternatively, a service provider and its supporting code may be packaged as a JAR file and deployed on the application class path. The advantage of developing a service provider in a module is that the provider can be fully encapsulated to hide all details of its implementation.
An application that obtains a service loader for a given service is indifferent to whether providers of the service are deployed in modules or packaged as JAR files. The application instantiates service providers via the service loader’s iterator, or via Provider objects in the service loader’s stream, without knowledge of the service providers’ locations.
2.1 模块化语句约定
模块化语句约定适用于项目已经模块化的情况,以java.sql.Driver为例,在模块化文件module-info.java中添加如下语句,就可以向应用提供指定的服务: