跳转至

什么是SPI,和API有啥区别

面试回答

面试官: 什么是 SPI?和 API 有啥区别?

你:

很多人搞混,其实用一句话就能区分:API 是你调别人的接口,SPI 是别人调你的实现。


SPI 全称 Service Provider Interface,是一种服务发现机制。 核心思想是:标准定义在核心库里,具体实现由第三方厂商提供,程序在运行时动态加载所有实现。最典型的例子就是 JDBC。你写的代码只依赖 java.sql.Driver 接口,不依赖任何具体数据库驱动。当你把 MySQL 的驱动 jar 包丢到 classpath 下,DriverManager 会自动通过 SPI 找到 com.mysql.cj.jdbc.Driver 并加载它,你的代码不用改一行。

SPI 的运作分三环:

  1. 标准 API:JDK 或框架定义接口(如 java.sql.Driver)。

  2. 实现方:第三方厂商写实现类,并在 META-INF/services/接口全限定名 文件里填上实现类的全限定名。

  3. 调用方:核心库用 ServiceLoader.load(接口.class) 加载所有实现,循环调用。

API 和 SPI 的本质区别是调用方向:

  • API:接口定义方也是实现方,比如你写的 UserService,你定义接口也实现它,别人调你的接口。调用方依赖接口和实现方。

  • SPI:接口定义方是“平台方”,只定义接口规范,不实现(或只给默认实现)。具体实现由“扩展方”提供,平台方用接口调扩展方的实现。调用方向反了。

一个拔高点:双亲委派破坏的典型场景。 SPI 的核心类(如 DriverManager)在 rt.jar 由 Bootstrap ClassLoader 加载,但它要回调由应用类加载器加载的实现类(如 com.mysql.jdbc.Driver)。根据双亲委派,父加载器加载的类没法直接调用子加载器加载的类,所以 ServiceLoader 内部用线程上下文类加载器(Thread Context ClassLoader)来打破这个限制。这是面试经常连着问的点。


Mermaid 时序图:API 与 SPI 的调用方向对比

deepseek_mermaid_20260612_ada0ac.png

总结一句话: 你写 JDBC 时只管调 DriverManager,MySQL 的 Driver 怎么被找到的?就是 SPI 在干活——平台定义接口,实现方扩展,运行时动态发现。