type
status
date
slug
summary
tags
category
icon
password
原文
SPI(Service Provider Interface) 服务提供者的接口。JDK 内置的一种服务提供发现机制,允许程序在外部动态制定具体实现。
主要是被框架开发人员使用的一种技术。
术语
Service : 一个公开的接口或抽象类,定义了一个抽象的功能模块.
Service Provider: Service 接口的一个实现类.
ServiceLoader: SPI机制的核心组件,负责在运行时发现并加载 Service Provider.
Java SPI运行流程

调用方除了提供调用方实现的接口或抽象类,还要使用加载器,去加载实现方的 Class。如果有特殊的加载需求则可以使用自定义加载器。JDK提供了一个默认的ServiceLoader(java.util.ServiceLoader)。
实现方(Service Provider)实现该接口或者抽象类,编写具体的代码逻辑。Service Provider 须在
META-INF/service
提供实现的描述信息,且以 jar 的方式提供给调用方。调用方会通过该描述文件来加载该 class。SPI与双亲委派机制
SPI机制也解决了 Java 类加载体系中的双亲委派机制。
以 JDBC 为例,JDK 提供了 Driver(java.sql.Driver) 接口,交由数据库驱动厂商自行去实现。
Driver接口位于 rt.jar中
,由启动类加载器(BootStrapClassLoader)加载。具体的 JDBC驱动实现如 com.mysql.cj.jdbc.Driver 是第三方jar,由应用加载器(AppClassLoader)加载,由于“双亲委派的限制”,父类加载器加载的类无法访问子类加载器加载的类,所以SPI 通过上下文类加载器饶过该限制。本质是理应由BootStrap类加载的 Class 反向委托给子类去加载。这样在编写JDBC时,开发者只需要将驱动 JAR 包放在类路径下,SPI 机制就能在运行时动态发现并加载这些实现。SPI设计
SPI 是基于面向接口编程,能够优雅的实现模块之间的解耦。
通过面向接口、配置文件、反射技术来达到解耦合的效果。
应用场景:JDBC SLFJ……
API&SPI
广义上来说他们都属于接口
API(Application Programming Interface): 当实现方提供了接口和实现,通过实现方提供的接口拥有实现的能力
SPI(Service Provider Interface): 调用方提供了接口,由调用方确定了接口规则,实现方需针对该接口进行实现从而提供服务

Java SPI & SpringBoot 自动配置
- SpringBoot 可以基于引入的依赖 JAR包实现自动装配
- 提供了自动配置功能的依赖 JAR ,通常称之为 starter,例如
mybatis-spring-boot-starter

Dubbo SPI
JDK SPI的限制
- 在查找扩展实现类的过程中,需要遍历SPI 配置文件中定义的所有实现类,该过程中会将这些实现类全部实例化。
- 如果 SPI 配置文件中定义了多个实现类,只需要使用其中一个实现类时,只需要使用其中一个实现类时,就会生成不必要的对象。
当然这里说的 SPI 限制是标准的 SPI 实现,通过自定义 ServiceLoader 逻辑可以规避。
dubbo框架也定制了SPI,该机制是属于 Dubbo 的自动装配机制。
dubbo 作为一个 RPC 框架,它对扩展性有要求,例如RPC 注册中心可以使用 nacos 或者 zookeeper,通过 SPI 机制可以实现按需加载,实现任意替换组件,这极大提高了扩展性。
dubbo SPI 支持适配器模式,通过@Adaptive 注解来实现自适应扩展。
配置文件分类
按照配置文件的用途,分为三类目录。
- META/services/ : 该目录下的 SPI 配置文件用来兼容 JDK SPI
- META/dubbo/ : 该目录下用于存放用户自定义 SPI 配置文件
- META/dubbo/internal/ : 该目录用于存放 dubbo 内部使用的 SPI 配置文件。
dubbo将SPI配置文件改成了 KV 格式
dubbo 示例
SPI注解标识该接口为一个扩展接口。其中value 表示默认的扩展名
Adaptive注解
dubbo SPI 支持自适应扩展。
通过 URL参数获取对应的值,来实现动态扩展。
Consumer通过扩展器加载该接口时,会自动为该接口自动生成一个自适应类 例如类似
AdaptiveMessageService$Adaptive
)。ExtensionLoader
dubbo-common模块中的
ExtensionLoader
类是 实现 dubbo SPI的核心类,它会去加载对应目录的配置文件,然后将创建对应的实例放入缓存当中。加载配置文件
LoadingStrategy 描述加载策略接口,有三个实现类,描述了三种不同的加载策略
主要的作用是加载 META-INF/dubbo/internal/ 、 META-INF/services/ META-INF/dubbo 目录下的文件// -
1.
DubboLoadingStrategy
用于加载用户自定义的 META-INF/dubbo 文件。2.
ServicesLoadingStrategy
加载 META-INF/services/目录下的配置文件,用于处理标准 Java SPI机制服务的加载。3.
DubboInternalLoadingStrategy
内置的加载策略 用于加载 dubbo 内置的扩展,位于 dubbo-common 模块下的META-INF/dubbo/internal/目录下的配置。Adaptive注解
dubbo SPI 支持自适应扩展。
通过 URL参数获取对应的值,来实现动态扩展。
Consumer通过扩展器加载该接口时,会自动为该接口自动生成一个自适应类 例如类似
AdaptiveMessageService$Adaptive
)。ExtensionLoader
dubbo-common模块中的
ExtensionLoader
类是 实现 dubbo SPI的核心类,它会去加载对应目录的配置文件,然后将创建对应的实例放入缓存当中。加载配置文件
LoadingStrategy 加载策略
LoadingStrategy为接口 有三个实现类,描述了三种不同的加载策略
主要的作用是加载 META-INF/dubbo/internal/ 、 META-INF/services/ META-INF/dubbo 目录下的文件// -
1.
DubboLoadingStrategy
用于加载用户自定义的 META-INF/dubbo 文件2. ServicesLoadingStrategy
3.
DubboInternalLoadingStrategy
内置的加载策略 用于加载 dubbo 内置的扩展,位于 dubbo-common 模块下的META-INF/dubbo/internal/目录下的配置- Author:newrain-zh
- URL:https://alex.sh.cn/article/spi
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!