我是靠谱客的博主 爱听歌板栗,这篇文章主要介绍SPI服务扩展机制,现在分享给大家,希望可以做个参考。

SPI服务扩展机制


SPI全称为service-provider,中文意思是服务扩展机制。

SPI:是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。
也可以这样理解:SPI是“基于接口的编程+策略模式+配置文件”组成实现的动态加载机制。

可能比较抽象,等看完就能理解了。

使用到SPI的例子:

  • Servlet规范
    Servlet3.0规范启动ServletContainerInitializer启动流程,在Servlet中就使用到了SPI,servlet-api Jar只是定义了一些规范。服务的具体实现由具体的Web容器(Tomcat / Jboss / Jetty)去实现。
  • JDBC规范
    JDBC规范也是只定义了一些接口和类,具体服务的实现由具体的数据库厂商(mysql驱动 / Oracle驱动)去实现。
使用SPI的机制的要求(重点):

1. 当服务提供者提供了接口的一种具体实现后,在工程中的META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名。
2. 接口实现类所在的工程classpath中:
3. 主程序通过java.util.ServiceLoader动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名。
4. SPI的实现类必须具有一个不带参数的构造方法

下面举个例子:
定义了一个文件上传的接口

复制代码
1
2
3
4
5
6
7
8
9
10
11
package com.zlin.spi; /** * SPI * 上传文件接口 binary name */ public interface IUpload { void upload(); }

然后Img PDF Txt文件的实现类都重写upload方法

复制代码
1
2
3
4
5
6
7
8
9
10
package com.zlin.spi; public class ImgUpload implements IUpload{ @Override public void upload() { System.out.println("上传图片"); } }
复制代码
1
2
3
4
5
6
7
8
9
10
package com.zlin.spi; public class PdfUpload implements IUpload{ @Override public void upload() { System.out.println("上传PDF"); } }
复制代码
1
2
3
4
5
6
7
8
9
10
package com.zlin.spi; public class TxtUpload implements IUpload{ @Override public void upload() { System.out.println("上传txt文件"); } }

然后在工程中的META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名。
在这里插入图片描述
再通过java.util.ServiceLoader动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名。

先看效果
在这里插入图片描述
去查看ServiceLoader的源码会发现,

复制代码
1
2
ServiceLoader<IUpload> iUploads = ServiceLoader.load(IUpload.class);

会去获取线程上下文类加载器,然后把我们的接口类型保存到ServiceLoader变量中,最后创建一个懒加载的迭代器。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
返回我们懒加载的迭代器对象

复制代码
1
2
Iterator<IUpload> iUploadIterator = iUploads.iterator();

iUploadIterator.hasNext():会读取"META-INF/services/com.zlin.spi.IUpload"的内容,然后把文件中的第一个值(第一个实现类的全限定名)保存到nextName变量。
第二次把第二个值保存到newxtName变量(第二个实现类的全限定名)
依次下去…

复制代码
1
2
iUploadIterator.hasNext():

iUploadIterator.next() 得到的是接口的实现类的实例, 会调用 c = Class.forName(cn, false, loader);

复制代码
1
2
iUploadIterator.next().upload();

在这里插入图片描述
hasNext()和next()的源码这里就不贴上来了,因为很简单也很好理解。
要是真的想学习的小伙伴,建议去自己动手敲一遍,进到hasNext()和next()里面打断点,整个流程走一遍,就全都懂了。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.zlin.spi; import java.util.Iterator; import java.util.ServiceLoader; public class SpiTest { public static void main(String[] args) { testSpi(); } private static void testSpi() { // 第一步:把我们的接口类型保存到ServiceLoader变量中 // 第二步:创建一个我们 懒加载的迭代器 // 1.保存了IUpload接口到service 2.保存线程上下文类加载器(AppClassLoader) 3.创建一个懒加载的迭代器LazyIterator ServiceLoader<IUpload> iUploads = ServiceLoader.load(IUpload.class); // 返回我们懒加载的迭代器对象 Iterator<IUpload> iUploadIterator = iUploads.iterator(); // iUploadIterator.hasNext(): 读取"META-INF/services/接口全类名"的内容 // 第一次把"META-INF/services/接口全类名"文件中的第一个值保存到newxtName变量 (第一个实现类的全限定名) // 第二次把第二个值保存到newxtName变量(第二个实现类的全限定名) 依次下去... while (iUploadIterator.hasNext()) { // iUploadIterator.next() 得到的是接口的实现类的实例 // iUploadIterator.next() 会调用 c = Class.forName(cn, false, loader); // 会触发类的初始化 ==> 调用类的静态代码块 初始化只会触发一次 iUploadIterator.next().upload(); } } }

SPI在mysql-connector-java中的使用:
在这里插入图片描述
上面涉及到的代码地址:
https://github.com/zhonglinliu123/MyBlogCode/tree/master/java/SPI

最后

以上就是爱听歌板栗最近收集整理的关于SPI服务扩展机制的全部内容,更多相关SPI服务扩展机制内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(84)

评论列表共有 0 条评论

立即
投稿
返回
顶部