⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 www.cnblogs.com/happyframework/p/3325560.html 「幸福框架」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

背景

第一次听说 SPI 是阅读《软件框架设计的艺术》,以后陆续在 JDBC 和 SpringBoot 中发现了以这种形式组织代码的方式,本位给出为什么要区分 SPI 和 API 的一个思考过程。

从面向接口编程说起

我们在“调用方”和“实现方”之间引入了“接口”,上图没有给出“接口”应该位于哪个“包”中,从纯粹的可能性上考虑,我们有三种选择:

  1. “接口”位于“调用方”所在的“包”中。
  2. “接口”位于“实现方”所在的“包”中。
  3. “接口”位于独立的“包”中。

下面让我们依次分析这三种可能性,如果现实中确实有这种可能性,不如我们就为其起个名字以方便交流。

“接口”位于“调用方”所在的“包”中

我们先想象一个场景,以仓储的接口为例:

我们将“仓储接口”放置于“领域层”这个“包”中,实现放在一个独立的“包”中,我们看DDD大师的实现都是这样子,现在来思考一下为什么这么做。

“领域层”的“领域服务”会依赖“仓储接口”,“仓储接口”也会依赖“聚合根”,这两者都是除了“实现依赖”之外的依赖关系,如果将“接口”放到“仓储实现”中就丧失了面向接口编程的意义(编译也不会通过),如果放到“独立层”中呢?会编译不通过,出现双向依赖了。

对于类似这种情况下接口,我们将其称为“SPI”,全程为:service provider interface,“SPI”的规则如下:

  1. 概念上更依赖调用方。
  2. 组织上位于调用方所在的包中。
  3. 实现位于独立的包中。
  4. 常见的例子是:插件模式的插件。

“接口”位于“实现方”所在的“包”中

我们先想象一个场景,以Unity提供的IUnityContainer接口为例,除了维护这个框架的团队之外,我们没有发现谁实现了这个接口,虽然理论上是可以实现这个接口的(如果能实现的话,我们何不自己弄额Ioc容器呢?)。

对于类似这种情况下的接口,我们将其称作为“API”,“API”的规则如下:

  1. 概念上更接近实现方。
  2. 组织上位于实现方所在的包中。
  3. 实现和接口在一个包中。

“接口”位于独立的“包”中

这里就不说场景了,如果一个“接口”在一个上下文是“API”,在另一个上下文是“SPI”,那么你就可以这么组织。

需要注意的事项

不管是 SPI 或 API,接口都是可以组织到独立的“包”中,这么做是否有意义,自己来做出决定了。

SPI 和 API 也不一定是接口,我这里都是指狭义的具体的接口。

场景图

每一次思考都伴随着收获,也离不开和朋友们的交流,天更蓝了。

SPI 接口:

  1. 定义:SPI 是一种服务提供者接口,它允许在运行时加载不同的服务实现。

  2. 使用场景:

  • 模块化设计:当系统需要高度模块化,且希望将核心功能与具体实现分离时。
  • 可插拔架构:需要支持多种服务实现,并且可以在不修改代码的情况下替换或增加新的服务实现。
  • 服务发现:在运行时根据配置或服务注册表动态发现和加载服务。
  • 微服务架构:在微服务架构中,SPI 可用于服务间的动态交互和集成。
  1. 优点:
  • 提供了一种机制来在运行时选择和加载服务实现,增加了系统的灵活性和可扩展性。
  • 支持服务的热插拔,无需重启系统即可更换服务实现。

API 接口:

  1. 定义:API 是一组预定义的函数、协议和工具,用于构建软件应用,它定义了软件组件之间交互的契约。

  2. 使用场景:

  • 客户端和服务器交互:当需要设计客户端和服务器之间的通信协议时。
  • 库和框架:提供给开发者使用的库或框架的公共接口。
  • 第三方集成:需要与第三方系统或服务进行集成。
  • 内部组件通信:在大型系统中,不同组件或模块之间的交互。
  1. 优点:
  • 为开发者提供了清晰的接口文档和规范,易于理解和使用。
  • 有助于保持系统的稳定性,因为 API 变更需要遵循版本控制和兼容性规则。
  • 促进了代码的重用和模块化。

如何选择?

选择使用 SPI 还是 API 的考虑因素:

  • 扩展性:如果需要在不修改代码的情况下扩展功能,SPI 更合适。
  • 交互性:API 更适合定义系统内部或系统之间的稳定交互接口。
  • 动态性:SPI 允许在运行时动态发现和加载服务,而 API 通常在编译时就已经确定。
  • 安全性和稳定性:API 由于其稳定性和可预测性,通常更受青睐。SPI 虽然灵活,但可能引入运行时错误。
  • 版本控制和兼容性:API 变更需要考虑版本控制和向后兼容性,而 SPI 可以通过服务版本协商来处理兼容性问题。

架构是“取舍”,而非“银弹”。

文章目录
  1. 1. 背景
  2. 2. 从面向接口编程说起
  3. 3. “接口”位于“调用方”所在的“包”中
  4. 4. “接口”位于“实现方”所在的“包”中
  5. 5. “接口”位于独立的“包”中
  6. 6. 需要注意的事项
  7. 7. 场景图
    1. 7.0.1. SPI 接口:
    2. 7.0.2. API 接口:
    3. 7.0.3. 如何选择?