Java SPI
概述
Java spi 提供了一种应用扩展的方式,他可以让服务的定义者和实现者分离,在运行时通过 classpath 动态加载实现者。服务的定义者一般是一个或者多个接口,服务提供者是实现了服务定义者定义的接口,分离则可以表现为他们通常在不同的 jar 包。
SPI 的构成四要素
SPI 由以下四个要素构成的:
- Service
- Service Provider Interface
- Service Provider
- ServiceLoader
下面我们具体讲解下各个要素含义:
Service
服务由一个或者多个接口和公开类组成,这些对于使用服务的客户端来说就是所谓的 API。
Service Provider Interface
Service Provider Interface(以下简称 SPI) 是接口或者抽象类,他们提供了服务访问的接口,也可以认为是服务的入口点。如果服务只有一个接口或者抽象类,则可以认为 Service 和 SPI 是同一个东西。
Service Provider
服务提供者实现了 SPI。为了让 ServiceLoader 准确识别和更高效的加载 SPI 实现的类,需要在资源目录下的 META-INF/services 中放入一个以 SPI 接口全限定名称命名的文件,文件内容是实现类的全限定名称。假如有个 spi,全限定名称为 net.verytools.FooService,实现类为 com.xx.FooServiceImpl,那么我们应该在 META-INF/services 目录下建一个名为 net.verytools.FooService,文件的内容为 com.xx.FooServiceImpl。
ServiceLoader
ServiceLoader 用于发现服务提供者并加载服务提供者,使用方式很简单:
ServiceLoader<FooService> loader = ServiceLoader.load(FooService.class);
loader.iterator();
通过 Load 方法可以拿到一个或者多个服务提供者实例,具体取决于 classpath 下有多少个服务提供者。
实际场景
作为开发者我们经常和 SPI 打交道,但是可能会忽略他的存在,殊不知我们最常用的 sl4j 正是通过 SPI 机制让我们可以切换到不同的日志框架。sl4j 提供的 spi 是 org.slf4j.spi.SLF4JServiceProvider
,以 logback 为例,在 META-INF/services 目录中 有一个名为 org.slf4j.spi.SLF4JServiceProvider 的文件,文件的内容为:
ch.qos.logback.classic.spi.LogbackServiceProvider
由此可见,Logback 实现了 sl4j 的 spi,因此 sl4j 可以和 logback 配合完成日志记录。这样做的好处对于开发者来说是显而易见的,我们无需关心每个日志框架的自身的接口,只需要利用 sl4j 提供的一套接口即可完成日志记录。
温馨提示:反馈需要登录