Java SPI 机制
什么是 SPI
SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。可以将服务接口与服务实现分离以达到解耦可拔插、大大提升了程序可扩展性。
1、制定统一的规范
2、服务提供商提供这个规范具体的实现,在自己 jar 包的 META-INF/services/ 目录里创建一个以服务接口命名的文件,内容是实现类的全命名
3、平台引入外部模块的时候,就能通过该jar包 META-INF/services/ 目录下的配置文件找到该规范具体的实现类名,然后装载实例化,完成该模块的注入。
Java SPI 应用实例
定义接口
package com.demo.lin.spi;
/**
* @author Lin = ̄ω ̄=
*/
public interface Download {
void download(String url);
}
实现类
package com.demo.lin.spi.impl;
import com.demo.lin.spi.Download;
/**
* @author Lin = ̄ω ̄=
*/
public class FileDownload implements Download {
@Override
public void download(String url) {
System.out.println("download " + url);
}
}
配置
com.demo.lin.spi.Download
内容
com.demo.lin.spi.impl.FileDownload
调用
package com.demo.lin.spi;
import java.util.ServiceLoader;
/**
* @author Lin = ̄ω ̄=
*/
public class TestDemo {
public static void main(String[] args) {
ServiceLoader<Download> searches = ServiceLoader.load(Download.class);
for (Download search : searches) {
search.download("https://www.bilibili.com/video/BV1Vt4y1D7v8");
}
}
}
输出
Java SPI 的用途
其中一个典型案例就是 java.sql.Driver
java.sql.Driver
定义了一个接口,并没有具体实现,具体的实现都是由不同厂商来提供的。
当我们想连接 Mysql 时,引入 jar 包如 mysql-connector-java-5.1.45.jar
,在 META-INF/services
目录下会有一个名字为 java.sql.Driver
的文件:
com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver
我们在代码中写到
String url = "jdbc:mysql://localhost:3306/test";
Connection conn = DriverManager.getConnection(url,username,password);
在 DriverManager
内部使用 ServiceLoader.load(Driver.class)
,搜索 classpath
下以及 jar 包中所有的 META-INF/services
目录下的java.sql.Driver
文件,并找到文件中的实现类的名字,实例化具体的实现类。
Spring SPI机制
Spring 的 SPI 机制主要体现在 SpringBoot 中,当我们启动 SpringBoot 的 run()
方法,会调用类 SpringFactoriesLoader
下的 loadSpringFactories
方法,去加载 META-INF/spring.factories
下的配置。
public final class SpringFactoriesLoader {
/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
// ...
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
// ...
}
// ...
}
}