前言
在讨论什么是SPI前,先回忆一下什么是API
什么是API?
当实现方提供了接口和实现,我们可以通过调用实现方的接口从而拥有实现方给我们提供的能力,这就是 API。
这种情况下,接口和实现都是放在实现方的包中。调用方通过接口调用实现方的功能,而不需要关心具体的实现细节。
什么是SPI?
当接口存在于调用方这边时,这就是**SPI **。由接口调用方确定接口规则,由不同的厂商根据这个规则对接口进行实现,从而提供服务。
举个例子:
API:我是一个老板,我需要达到一个目标,然后我去找能帮我达到这个目标的实现方(多个),至于怎么实现的,我不管,我只看结果,也就是说接口和接口实现都由实现方来制定
SPI:我是一个老板,我需要达到一个目标,然后我去找能帮我达到这个目标的实现方,我制定一个实现标准,多个实现方根据老板
提供的实现标准去实现,也就是说接口是由老板(调用方)制定,而接口实现由实现方来制定
JDBC中的SPI
在 JDBC 使用过程中,有注册驱动这一步骤,代码如下:
Class.forName("com.mysql.jdbc.Driver");
现在,我们将那个案例中的这行代码给注释/删除掉。即 getConnection() 方法代码如下:
public static Connection getConnection() { Connection conn = null; //try { //Class.forName("com.mysql.jdbc.Driver"); try { conn = DriverManager.getConnection(JDBC_URL, USERNAME, PASSWORD); System.out.println(conn); } catch (SQLException e) { System.out.println("数据库连接失败"); } //} catch (ClassNotFoundException e) { //System.out.println("驱动包不存在"); //} return conn; }
会发现,Connection还是能够获取成功的,那为什么 JDBC 中不注册驱动还能连接数据库成功?就是SPI机制的作用了
看一下DriverManger的源代码
public class DriverManager { private DriverManager(){} static { loadInitialDrivers(); println("JDBC DriverManager initialized"); } ... }
在loadInitialDrivers()方法中:
会去查找 Driver 接口的服务类,文件路径是:META-INF/services/java.sql.Driver。创建文件里面的实现类
打开IDEA验证一下,去 External Libraries 找到我们的数据库依赖查看,确实如此
SPI机制简化了应用程序的代码,并提高了灵活性和可扩展性
通过SPI机制,不同的数据库供应商可以提供自己的驱动程序,而应用程序可以轻松地与这些不同的数据库进行交互。
Dubbo中的SPI
根据上面的案例,SPI的运行机制已经清楚了,再来看一下Dubbo中哪里用到了SPI
Dubbo SPI 逻辑封装在 ExtensionLoader 类,通过 ExtensionLoader,可加载指定实现类。
Dubbo SPI 所需配置文件需放置在 META-INF/dubbo 这个路径下,同样打开IDEA验证一下
举个例子,项目中需要配置不同的序列化器,由你自己(实现方)去实现由Dubbo(调用方)制定出来的接口