SPI示例学习

Service Provider Interface

它是从Java 6开始引入的,是一种基于ClassLoader来发现并加载服务的机制。

服务发现机制:通过在ClassPath路径下的META-INFO/services文件夹中查找文件,并自动加载文件里所定义的类。

SPI机制可以很好的解决不同框架之间的扩展问题。在java.util.ServiceLoader#load(Class clazz)完成了SPI的实现。

SPI机制能够使接口与具体的实现类解耦,可以根据实际的业务情况启用或替换具体组件。

一个标准的SPI,由3个组件构成,分别是:

  • Service:一个公开的接口或抽象类,定义了一个抽象的功能模块。
  • Service Provider:是Service接口的一个实现类。
  • ServiceLoader:是SPI机制中的核心组件,负责在运行时发现并加载Service Provider。

Java SPI的运行流程

SPI示例学习_第1张图片

Java SPI的三大规范要素:

  1. 规范的配置文件:
  • 文件路径:必须在JAR包中的META-INFO/services目录下
  • 文件名称:Service接口的全限定名
  • 文件内容:Service实现类(及Service Provider类)的全限定名。如果有过个实现类,那么每一个实现类在文件中单独占据一行
  1. Service Provider类必须具备无参的默认构造方法

Service接口的实现类,即Service Provider类,必须具备无参的默认构造方法。因为随后通过反射技术实例化它时,是不带参数的。

  1. 保证能加载到配置文件和Service Provider类
  • 方式一:将Service Provider的JAR包放到classpath中(最常用)
  • 方式二:将JAR包安装到JRE的扩展目录中
  • 方式三:自定义一个ClassLoader

示例:

有一家公司(company),它需要连接互联网,它定义一个连接网络的API(network-api),由联通(unicom-network)和电信(telecom-network)来提供网络服务。项目目录如下:

SPI示例学习_第2张图片

InternetService服务接口定义:

package com.lwy.it;

/**
 * 定义了SPI中的Service接口
 */
public interface InternetService {
    void connectInternet();
}

telecom-network模块,只有一个实现类:

package com.telecom.network;

import com.lwy.it.InternetService;

/**
 * TelecomNetwork 作为 Service Provider类,实现了InternetService接口
 */
public class TelecomNetwork implements InternetService {
    @Override
    public void connectInternet() {
        System.out.println("通过电信网络联网");
    }
}

注意:classpath下META-INF/service路径下,定义一个com.lwy.it.InternetService文件

文件名是Service接口的全限定名;文件内容是Service Provider类的全限定名,多个Service Provider用多行表示。

com.telecom.network.TelecomNetwork

unicom-network模块,有两个实现类:

package com.unicom.network;

import com.lwy.it.InternetService;

public class BeijingUnicomNetwork implements InternetService {
    @Override
    public void connectInternet() {
        System.out.println("通过北京联通网络联网");
    }
}
package com.unicom.network;

import com.lwy.it.InternetService;

public class UnicomNetwork implements InternetService {
    @Override
    public void connectInternet() {
        System.out.println("通过联通网络联网");
    }
}

注意:classpath下META-INF/service路径下com.lwy.it.InternetService文件

com.unicom.network.BeijingUnicomNetwork
com.unicom.network.UnicomNetwork

company模块中定义主方法:

package com.lwy.it;

import java.util.ServiceLoader;

public class Main {
    public static void main(String[] args) {
        // 使用ServiceLoader发现并加载服务
        ServiceLoader<InternetService> loader = ServiceLoader.load(InternetService.class);
        // 面向Service接口编程
        for (InternetService provider : loader) {
            provider.connectInternet();
        }
    }
}

通过分别引入maven依赖不同,打印的结果也不同。

    <dependency>
        <groupId>com.lwy.itgroupId>
        <artifactId>unicom-networkartifactId>
        <version>1.0-SNAPSHOTversion>
    dependency>
    <dependency>
        <groupId>com.lwy.itgroupId>
        <artifactId>telecom-networkartifactId>
        <version>1.0-SNAPSHOTversion>
    dependency>

你可能感兴趣的:(应用开发,spi)