【SPI】浅谈JDK中SPI技术

SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。

常见的 SPI 有 JDBC、日志门面接口、Spring、SpringBoot相关starter组件、Dubbo、JNDI等。

Java SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制。

 

 

要使用Java SPI,需要遵循如下约定:
1、当服务提供者提供了接口的一种具体实现后,在jar包的META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名;
2、接口实现类所在的jar包放在主程序的classpath中;
3、主程序通过java.util.ServiceLoder动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM;
4、SPI的实现类必须携带一个不带参数的构造方法。

 示例:

先创建四个maven项目,分别为spi-inter(定义标准服务接口)、spi-defaultInterImpl(服务提供方提供的默认实现)、spi-myInterImpl(调用方觉得默认实现不好使,自己写的实现)、spi-testSpi。

 

在spi-inter 中定义标准服务接口, 然后将项目打包。

1 package com.demo.spi_inter;
2 /**
3  *    服务方定义的标准服务接口
4  */
5 public interface Config {
6     
7     void loadConfig();
8 }

在spi-defaultInterImpl的pom.xml中添加依赖spi-inter,并进行对接口的实现。在src/main/java目录下新建META-INF/services目录,并在services中新建文件,文件名为接口的全限定名,

如示例:“com.demo.spi_inter.Config”,内容为接口实现类的全限定名。如示例:”com.demo.spi_defaultInterImpl.DefaultInterImpl“。然后将项目打包。

 1 package com.demo.spi_defaultInterImpl;
 2 
 3 import com.demo.spi_inter.Config;
 4 
 5 public class DefaultInterImpl implements Config{
 6 
 7     @Override
 8     public void loadConfig() {
 9         System.out.println("这是对接口的默认实现");
10     }
11 
12 }

在spi-testSpi的pom.xml文件中添加入spi-defaultInterImpl的依赖。使用 ServiceLoader 来加载配置文件中指定的实现。

 1 package com.demo.spi_testSpi;
 2 
 3 import java.util.Iterator;
 4 import java.util.ServiceLoader;
 5 
 6 import com.demo.spi_inter.Config;
 7 
 8 public class App {
 9     public static void main(String[] args) {
10         System.out.println("开始加载");
11         ServiceLoader<Config> loader = ServiceLoader.load(Config.class);
12         Iterator<Config> iterator = loader.iterator();
13         while (iterator.hasNext()) {
14             Config config = (Config) iterator.next();
15             config.loadConfig();
16         }
17     }
18 }

运行,可得结果:

 

此时,个人调用方觉得默认的实现不太好,于是自己编写了一个新的实现spi-myInterImpl。并创建了相对应的目录结构及文件/META-INF/services/com.demo.spi_inter.Config,文件内容为个人实现的全限定名。打包,将依赖引入到spi-testSpi中。

 1 package com.demo.spi_myInterImpl;
 2 
 3 import com.demo.spi_inter.Config;
 4 
 5 public class MyInterImpl implements Config{
 6 
 7     @Override
 8     public void loadConfig() {
 9         System.out.println("这是个人写的实现");
10     }
11 }

运行spi-testSpi,可以看见控制台输出:

SPI技术的优劣:

优点
解耦,使得第三方服务模块的装配控制的逻辑与调用者的业务代码分离,而不是耦合在一起。应用程序可以根据实际业务情况启用框架扩展或替换框架组件。

缺点

虽然ServiceLoader也算是使用的延迟加载,但是基本只能通过遍历全部获取,也就是接口的实现类全部加载并实例化一遍。如果你并不想用某些实现类,它也被加载并实例化了,这就造成了浪费。获取某个实现类的方式不够灵活,只能通过Iterator形式获取,不能根据某个参数来获取对应的实现类。

多个并发多线程使用ServiceLoader类的实例是不安全的。

posted @ 2020-03-12 17:21  墨猴  阅读(1074)  评论(0编辑  收藏  举报