SPI简介

安稳与你 提交于 2020-03-10 13:49:00

SPI简介

SPI全称Service Provider Interfaces,用于发现接口的实现。在jdbc、日志、dubbo的设计中都使用SPI用于服务的发现。

SPI是用来干嘛的呢?

可以定义接口标准,服务提供商只需要实现接口,并在resource目录下配置相应的文件,我们就可以直接通过ServiceLoader.load(接口名.class)的方法来加载具体的服务实现类,而无需去关心具体的服务实现。

这样就可以实现一种可插拨式的服务提供。比如,java.sql.Driver是定义的jdk中的spi接口,当我们引入的是mysql时,它就可以加载mysql-connector服务。

mysql-connector-java中Driver类,就是实现了spi服务接口,当引入该jar包时,我们调用DriverManager.getConnection方法时,就可以获取到mysql的连接。同样,也可以引入oracle的jar包,也就获取到oracle的客户端连接信息。这种插件式服务,极大的方便了开发人员。服务提供者只需实现标准接口,并配置好相应的配置文件,而使用者,只需通过标准的获取连接代码,就可以获取到相应的connection信息。(DriverManager.getConnection)

SPI具体获取服务的过程

流程如下:

  1. 服务方提供一种接口的实现,再classpath下的META-INF/services/目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类。
  2. 调用方引入jar包后,想用到服务,需要调用ServiceLoader.load()方法,获取到服务接口的实现类实例。
  3. ServiceLoader.load() 方法会读取厂商jar包中META-INF/services/目录下的文件。
  4. ServiceLoader使用迭代器去遍历(实际上是调用LookupIterator的迭代器),调用hasNext方法时,会加载对应的配置文件并解析。
  5. 调用next方法时进行实例化并缓存。
  6. 在循环之中会找到自己想要的服务。

以厂商Oracle、MySQL提供的DataSource为例:

第一步:Oracle、MySQL提供了一种DataSource接口的实现之后,需要在classpath下的META-INF/services/目录里创建一个以服务接口(org.cellphone.api.DataSource)命名的文件,这个文件里的内容就是这个接口的具体的实现类,文件内容为:

org.cellphone.oracle.DataSourceImpl

在这里插入图片描述
第二步:当用户需要这个服务的时候,就可以通过查找厂商Oracle、MySQL提供的jar包中(一般都是以jar包做依赖)的META-INF/services/中的配置文件,发现org.cellphone.api.DataSource文件。

第三步:调用ServiceLoader.load(DataSource.class),开始遍历META-INF/services/目录下的所有文件,这里我们只展示了一个文件。

第四步:调用hasNext方法,加载对应的配置文件并解析。

第五步:调用next方法时进行实例化并缓存,这里所有的配置文件都会被实例化,包括自己先要的类。

第六步:在循环中实例化自己想要的DataSourceImpl类。

注意:所有的配置文件只会加载一次,服务提供者也只会被实例化一次,重新加载配置文件可使用reload方法。

SPI缺点

通过上面的解析,可以发现,我们使用SPI查找具体的实现的时候,需要遍历所有的实现,并实例化,然后我们在循环中才能找到我们需要实现。这应该也是最大的缺点,需要把所有的实现都实例化了,即便我们不需要,也都给实例化了。

总结

  1. 定义标准的服务接口,接口服务提供者只需实现该接口,并配置相应的配置文件(META-INF/services),就能插件式的提供服务了。
  2. 该方式降低了服务提供者与服务使用者的耦合,它们只需要关注于各自服务即可,而无需关注其他。(高内聚、低耦合)
  3. spi服务通常是遍历了所有的接口实现,可能有接口实现我们并不需要加载,这样造成了内存的浪费。

链接:https://www.jianshu.com/p/ab3fbb0e85e6

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!