导言
在看双亲委派机制的时候,有人提到SPI
的概念,于是学习了一番。SPI
,即Service Provider Interface
,服务提供者的接口。这个SPI
接口是由服务端提供和调用的,这个接口定义了一些规范,但不提供具体的接口功能,功能由客户端决定,客户端根据需要实现这个接口,并进行配置,将接口的实现实例和配置打包部署在服务端,服务端不会直接调用接口的实现实例,而是 调用SPI
接口,其产生的行为取决于配置文件。
SPI
设计思想如下图所示:
SPI
接口一般由客户端和服务端共同确定(更多是服务端根据不同客户端的需求,形成一个兼容并包的接口)。客户端根据需求实现这个接口,得到一个自己的实例。服务端会有一个以这个SPI
接口为文件名的配置文件,配置文件当中指定服务端将调用哪个客户端的接口实例。
SPI
的本质是多态,多态的好处是在解耦的同时,实现了适配。
SPI的实现之ServiceLoader
Java是通过ServiceLoader
提供SPI
功能的,我们来模拟服务端、客户端的角色,理解SPI
是怎么工作的。
场景:不同的数据库想要通过Java连接,必须实现一个规范的接口,并提供相应的JDBC驱动。
一、服务端与客户端协商提供统一的SPI接口
新建一个Java项目,给出统一的SPI
接口,代码如下,其中有一个connect()
方法。
package com.java.db;
/**
* @ClassName DBDriver
* @Description TODO
* @Date 2021/1/11 22:19
*/
public interface DBDriver {
public String connect();
}
把这个项目打包成jar
包,记名为DBDriverInterface.jar
。
二、客户端提供带有配置文件的驱动类
新建一个Java项目,创建一个MySQLDriver
类,实现DBDriver
接口,因此这个项目中需要导入DBDriverInterface.jar
包,目录结构如下图所示:
代码如下:
package com.java.mysql.godriver;
import com.java.db.DBDriver;
/**
* @ClassName MySQLDriver
* @Description TODO
* @Date 2021/1/11 22:21
*/
public class MySQLDriver implements DBDriver {
public String connect() {
System.out.println("MySQL连接成功");
return "一个MySQL连接实例";
}
}
在resources
目录下,添加META-INF\services
目录,并创建文本文件,文件名为com.java.mysql.DBDriver
,就是SPI
接口的全名
,写入以下内容,这就是接口实现类的全名
:
com.java.mysql.godriver.MySQLDriver
把这个项目打包成jar
包,记名为MySQLDBDriver.jar
。
三、服务端利用ServiceLoader加载驱接口
利用ServiceLoader
加载接口的class
对象,就可以加载配置文件当中实现了这个接口的所有驱动类,并执行相应的动作。
新建一个Java项目,导入上述两个包:DBDriverInterface.jar
包和MySQLDBDriver.jar
包,并利用ServiceLoader
去加载MySQL数据库连接驱动,目录结构如下所示:
主类代码如下: