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的运行流程
Java SPI的三大规范要素:
- 规范的配置文件:
- 文件路径:必须在JAR包中的META-INFO/services目录下
- 文件名称:Service接口的全限定名
- 文件内容:Service实现类(及Service Provider类)的全限定名。如果有过个实现类,那么每一个实现类在文件中单独占据一行
- Service Provider类必须具备无参的默认构造方法
Service接口的实现类,即Service Provider类,必须具备无参的默认构造方法。因为随后通过反射技术实例化它时,是不带参数的。
- 保证能加载到配置文件和Service Provider类
- 方式一:将Service Provider的JAR包放到classpath中(最常用)
- 方式二:将JAR包安装到JRE的扩展目录中
- 方式三:自定义一个ClassLoader
示例:
有一家公司(company),它需要连接互联网,它定义一个连接网络的API(network-api),由联通(unicom-network)和电信(telecom-network)来提供网络服务。项目目录如下:
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.it</groupId>
<artifactId>unicom-network</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.lwy.it</groupId>
<artifactId>telecom-network</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>