Java_SPI思想
研究SpringBoot源码的时候偶遇SpringFactoriesLoader,即SPI思想(也可以说是一种SPI机制)。
1、SPI 思想是什么?
SPI 的全名为 Service Provider Interface 。SPI 思想 也可以叫做 SPI机制 ,它就是为某个接口寻找服务实现的机制。
2、为什么会有这个东西?
这个东西主要微服务用的多,因为这个思想是针对厂商或者插件的。在java.util.ServiceLoader
的API文档里有比较详细的介绍。
简单的总结下 Java SPI 机制的思想。我们系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块、jdbc模块的方案等。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。
为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。 Java SPI 就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外(通过约定的配置文件),在模块化设计中这个机制尤其重要。
同时起到解耦的作用。
3、SPI 具体怎么实现呢?
为了使用Java SPI ,需要遵循一种约定,该约定为: 当服务的提供者(厂商或者插件),提供了服务接口的一种实现之后,在jar包的META-INF/services/
目录里同时创建一个以服务接口全类名命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/
里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。 基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。jdk提供服务实现查找的一个工具类:java.util.ServiceLoader
4、话不多说,代码走起
4.1、支付案例
一个订单系统有一个支付模块,是基于接口编程的。支付的实现可能是基于支付宝支付、也可以基于微信支付等等
建立一个maven项目:父项目 java-spi、子项目(包含四个模块) main-pay、services-common、wx-pay、zfb-pay
各个模块细节:
服务接口定义:
package com.xd.service;
/**
* @author dee
* @create 2020-11-19 0:59
*/
public interface PayService {
public void pay();
}
微信厂商实现模块:
package com.xd.impl;
import com.xd.service.PayService;
/**
* @author dee
* @create 2020-11-19 1:15
*/
public class WxPayService implements PayService {
@Override
public void pay() {
System.out.println("微信支付!");
}
}
支付宝厂商实现模块:
package com.xd.impl;
import com.xd.service.PayService;
/**
* @author dee
* @create 2020-11-19 1:01
*/
public class ZfbPayService implements PayService {
@Override
public void pay() {
System.out.println("支付宝支付!");
}
}
主程序模块:
package com.xd;
import com.xd.impl.WxPayService;
import com.xd.service.PayService;
import java.util.Iterator;
import java.util.ServiceLoader;
/**
* @author dee
* @create 2020-11-19 1:17
*/
public class PayTest {
public static void main(String[] args) {
//通过load方法新建对象
ServiceLoader<PayService> serviceLoader = ServiceLoader.load(PayService.class);
Iterator<PayService> iterator = serviceLoader.iterator();
while (iterator.hasNext()){
PayService payService = iterator.next();
payService.pay();
}
}
}
当需要使用微信支付的时候,只需要在主程序pom文件导入微信模块的pom依赖即可
<dependency>
<groupId>org.example</groupId>
<artifactId>wx-pay</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
需要注意的是:厂商接口实现模块,有一个路径为META-INF/services/
文件名为 com.xd.service.PayService
的文件,里面内容为微信实现类的全类名,即SPI约定
com.xd.impl.WxPayService
执行主程序输出:
微信支付!
当需要使用支付宝支付只需要导入支付宝实现模块pom依赖即可
<dependency>
<groupId>org.example</groupId>
<artifactId>zfb-pay</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
执行主程序输出:
支付宝支付!
下面这些技术也有使用SPI思想,这里就不在这里扩展了!
4.2、DriverManager
DriverManager是jdbc里管理和注册不同数据库driver的工具类。
4.3、Spring
Spring中运用到spi思想的地方也有很多,下面随便列举几个
scan、scop、ConfigurableBeanFactory
4.4、SPI思想可是SpringBoot的灵魂
SpringFactoriesLoader工具类
4.5、Hotspot
参考资料:
https://zhuanlan.zhihu.com/p/28909673
https://www.bilibili.com/video/BV13A411B7JH?from=search&seid=17307742882483866414