一、什么是JAVA SPI ?
SPI的全名为Service Provider Interface.SPI的思想是:我们系统里抽象的各个模e块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块、jdbc模块的方案等。在面向对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里指明,这就需要一种服务发现机制。 java spi就是提供这样的一个机制,为某个接口寻找服务实现的机制。将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。
二、JAVA SPI的约定:
当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。 基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。jdk提供服务实现查找的一个工具类:java.util.ServiceLoader.
java.sql.Driver接口的sqlite数据库驱动实现类:
三、demo实现:
1、新建接口类 ISPIService,void hello();2、两个实现类SPIServiceImp1、SPIServiceImp2分别实现ISPIService接口并重写void hello()方法;
3、按照java spi约定建立services文件:4、使用java提供的工具类ServiceLoader,获取接口的实现并执行:
至此我们完成了一个简单的demo,可以发现,这样的方式实现了服务的抽象接口与服务的实现类解耦;模块的可插拔。
四、JAVA SPI源码执行过程分析:
五、JAVA SPI优缺点:
1、优点:
不需要改动源码就可以实现扩展,解耦。
实现扩展对原来的代码几乎没有侵入性。
只需要添加配置就可以实现扩展,符合开闭原则
2、缺点:
只能遍历所有的实现,并全部实例化。
配置文件中只是简单的列出了所有的扩展实现,而没有给他们命名。导致在程序中很难去准确的引用它们。
扩展如果依赖其他的扩展,做不到自动注入和装配。
扩展很难和其他的框架集成,比如扩展里面依赖了一个Spring bean,原生的Java SPI不支持。
六、其他框架的SPI 机制:
1、Dubbo SPI 机制:
Dubbo 并未使用 Java SPI,而是重新实现了一套功能更强的 SPI 机制。
Dubbo SPI 的相关逻辑被封装在了 ExtensionLoader 类中,通过 ExtensionLoader,我们可以加载指定的实现类。Dubbo SPI 所需的配置文件需放置在 META-INF/dubbo 路径下。
与Java SPI 实现类配置不同,Dubbo SPI 是通过键值对的方式进行配置,这样我们可以按需加载指定的实现类。另外,在测试 Dubbo SPI 时,需要在 Robot 接口上标注 @SPI 注解。
2、spring boot SPI机制:
在springboot的自动装配过程中,最终会加载META-INF/spring.factories文件,而加载的过程是由SpringFactoriesLoader加载的。从CLASSPATH下的每个Jar包中搜寻所有META-INF/spring.factories配置文件,然后将解析properties文件,找到指定名称的配置后返回。