我是靠谱客的博主 美好酒窝,最近开发中收集的这篇文章主要介绍Java SPI机制应用实战一、SPI机制简介二、SPI应用三、分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

  • 一、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#hasNextjava.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应用三、分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(52)

评论列表共有 0 条评论

立即
投稿
返回
顶部