SPI(Service Provider Interface)是一种服务发现机制,它允许应用程序在运行时动态地发现和加载实现某个接口的服务提供者。这里有两个关键词需要你特别关注,分别是“运行时”和“动态”。
作为一门面向对象语言,Java 虽然也提供了类似多态这样的动态机制,但是那是在编译时就已经决定了具体的服务提供方的一种方式,虽然能满足我们大部分关于动态的需求,但是复杂的现实环境依然存在需要在运行时才能决定服务提供者的需求,而这正是 SPI 的价值所在。
SPI 和 API 有什么不同?
SPI 看上去和我们熟知的 API 很像,所以当很多人谈到 SPI 时,经常拿 API 来和它作对比,下面我们就比较下两者的异同。相信对比之后,你对 SPI 的价值会有更深刻的理解。
SPI 是一种编程模式,用于定义和实现可插拔的服务提供者。它允许开发人员为特定的接口定义多个实现,并在运行时选择使用哪个实现。
API 目的是为开发人员提供一种简化和标准化的方式来使用现有功能和服务,以便更轻松地构建应用程序。
开发者在使用 SPI 时,需要实现特定接口,并把它注册到应用程序中。然后在运行时应用程序可以通过配置文件或其他机制选择使用哪个实现。
而使用 API 时,开发人员只需要了解 API 的规则和约定,并使用它提供的方法和函数来调用所需的功能就可以了。
所以 SPI 常用于框架和库的设计中,以便开发者根据自己的需求扩展和定制功能,而 API 常用于公共服务和第三方库的设计中,以便其他开发人员可以使用和集成这些功能。
SPI 原理
基于“约定大于配置”的设计思想,SPI 通过在类路径下预先定义好接口和服务提供者的实现类,使应用程序可以在运行时自动发现和使用这些服务,而无需手动配置。
SPI 约定中的约定指的是什么?
前面我们介绍原理的时候,说过 SPI 秉承的是约定大于配置的设计理念,那你有没有想过这里提到的约定指的是什么?
1. SPI 的实现类实现一个不带参数的构造函数。
2. 在 jar 包的 META-INF/services 目录里创建一个文件,文件以接口的全限定名进行命名,文件内容是实现类的全限定名,方便 SPI 自动查找和加载实现类。
1. 把包含这个接口实现类的 jar 包放到主程序的 classpath 里面。
通过这种“接口的编程+策略模式+配置文件”的方式,SPI 打破了双亲委派模型,实现了另一种类动态加载机制,使我们可以在运行时动态决定由哪个服务方提供服务。
SPI 背后设计思想
SPI 机制其实是对软件开发原则和 IoC 思想很好的应用。
开放封闭原则(Open-Closed Principle)
如何设计一个好的软件是大家一直在思考的问题,也沉淀了很多好的解决方案,比如设计模式等,设计模式的基础其实就是我们现在要聊的 SOLID 软件开发原则。
而 SPI 正是开放封闭原则很好的实践案例。开放封闭原则要求软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。这意味着我们应该通过添加新的代码来扩展功能,而不是修改已有的代码。通过遵循这一原则,我们可以减少对已有代码的影响,提高代码的可维护性和可复用性。而 SPI 通过运行时动态的特性,在保障接口稳定的基础上,提供了更加灵活的扩展机制,所以在 Netty、Duboo、Spring 等各种平台类的应用上都能看到 SPI 的身影。
控制反转(Inversion of Control,简称 IoC)
IoC 也是一种软件设计原则,它将对象的创建、依赖关系的管理从应用程序代码中解耦出来,交给容器负责。从而实现对象之间的解耦。控制反转的核心思想是“反转”,就是把原本由开发人员控制的对象的创建和依赖关系的管理交给容器来控制。SPI 正是 IoC 的思想的一个实践案例,通过提供灵活的接口实现服务定位机制,将应用的装配控制权外移到程序之外,实现了接口定义与实现的解耦,为平台化、模块化设计提供了很好的底层机制。
此文章为9月Day12学习笔记,内容来源于极客时间《云时代JVM实战 》,强烈推荐该课程
网友评论