概述
文章目录
- 一、SPI机制简介
- 二、SPI应用
- 1、定义接口与实现类
- 2、创建服务动态装载文件
- 3、服务的调用
- 三、分析
一、SPI机制简介
SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制,可以轻松实现面向服务的注册与发现,完成服务提供与使用的解耦,并且可以实现动态加载。
引入服务提供者就是引入了SPI接口的实现者,通过本地的注册发现获取到具体的实现类,轻松可插拔,SPI实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载,为某个接口寻找服务实现的机制。
二、SPI应用
利用SPI机制,快速实现一个可插拔服务,实现步骤也简单。
1、定义接口与实现类
定义一个接口(SPIService):
package org.spi.service;
public interface SPIService {
void doSomeThing();
}
创建该接口对应的实现类(SPIServiceImpl):
package org.spi.service.impl;
import org.spi.service.SPIService;
public class SPIServiceImpl implements SPIService {
@Override
public void doSomeThing() {
System.out.println("call SPIServiceImpl.doSomeThing()");
}
}
2、创建服务动态装载文件
根据SPI机制的约定,在META-INF/services
目录下创建一个以提供服务的接口/类的全限定名命名的文件
,文件内容为这个服务的实现类的全限定名,如有多个实现类则换行进行写入。
* <h2> Deploying service providers on the class path </h2>
*
* A service provider that is packaged as a JAR file for the class path is
* identified by placing a <i>provider-configuration file</i> in the resource
* directory {@code META-INF/services}. The name of the provider-configuration
* file is the fully qualified binary name of the service. The provider-configuration
* file contains a list of fully qualified binary names of service providers, one
* per line.
*
* <p> For example, suppose the service provider
* {@code com.example.impl.StandardCodecs} is packaged in a JAR file for the
* class path. The JAR file will contain a provider-configuration file named:
*
* <blockquote>{@code
* META-INF/services/com.example.CodecFactory
* }</blockquote>
*
* that contains the line:
*
* <blockquote>{@code
* com.example.impl.StandardCodecs # Standard codecs
* }</blockquote>
本例中,提供SPIService服务,实现类为SPIServiceImpl。所以在META-INF/services目录下创建一个以SPIService的全限定名命名的文件,内容则为其实现类的全限定名,如下图
3、服务的调用
SPI通过java.util.ServiceLoader
类进行加载,获取服务实例并调用相关方法。创建测试类如下:
public class SPITest {
public static void main(String[] args) {
ServiceLoader<SPIService> services = ServiceLoader.load(SPIService.class);
Iterator<SPIService> iterator = services.iterator();
while (iterator.hasNext()){
SPIService spiService = iterator.next();
spiService.doSomeThing();
}
}
}
通过运行该测试用例,可以看到服务实现成功调用,输出结果。
三、分析
在我们调用ServiceLoader.load()方法的时候,SPI并没有去查找并实例化服务,而是在java.util.ServiceLoader.LayerLookupIterator
中完成。
在获取服务的迭代器时(代码Iterator<SPIService> iterator = services.iterator();
),调用newLookupIterator()方法创建了lookupIterator1实例
本例中,创建的lookupIterator1实例实际由ModuleServicesLookupIterator和LazyClassPathLookupIterator这两个迭代器组成。进一步debug可以发现,ModuleServicesLookupIterator迭代器并没有服务实例,所以服务的实例由LazyClassPathLookupIterator进行加载。
当调用java.util.Iterator#hasNext
或java.util.Iterator#next
方法时,实际是调用了lookupIterator1实例对应的lookupIterator1.hasNext()
和lookupIterator1.next()
方法。lookupIterator1调用这两个方法,最终会通过java.util.ServiceLoader.LazyClassPathLookupIterator#hasNextService
方法调用java.util.ServiceLoader.LazyClassPathLookupIterator#nextProviderClass
加载服务实现类,源码如下。
可以看出,这个方法先读取META-INF/services/
目录下的服务文件,然后再加载服务实例。
最后
以上就是美好酒窝为你收集整理的Java SPI机制应用实战一、SPI机制简介二、SPI应用三、分析的全部内容,希望文章能够帮你解决Java SPI机制应用实战一、SPI机制简介二、SPI应用三、分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复