1.1什么叫SPI
SPI的全名为Service Provider Interface,单从字面可以理解为Service提供者接口,正如从SPI的名字去理解SPI就是提供给服务提供厂商与扩展框架功能的开发者使用的接口。
简单的总结下javaSPI机制的思想。我们系统里抽象的各个模块,往往有很多不同的实现方案。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。
java SPI就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。
1.2 SPI的用法
当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。
基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。
JDK提供服务实现查找的一个工具类:java.util.ServiceLoader。
1.3代码实现
1.1.1创建工程
首先创建如下几个工程:
1.1.2创建接口
在service-common工程中创建一个接口:
package cn.tx.service;
public interface PayService {
public void pay();
}
1.1.3为工程引入接口
在weixinPay和zhifubaoPay以及test中添加service-common依赖:
<dependencies>
<dependency>
<artifactId>service-common</artifactId>
<groupId>cn.tx.spingboot</groupId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
这样就可以在类中引入接口了。
1.1.4创建实现类
在weixinPay和zhifubaoPay中分别创建实现类实现PayService()接口:
package cn.tx.service.impl;
import cn.tx.service.PayService;
public class WeixinPay implements PayService {
@Override
public void pay() {
System.out.println("微信已付款");
}
}
package cn.tx.service.impl;
import cn.tx.service.PayService;
public class ZhifubaoPay implements PayService {
@Override
public void pay() {
System.out.println("支付宝已付款");
}
}
1.1.5创建实现类
在test中除了要引入接口外,也要引入实现类:
<dependency>
<!--或者weixinPay-->
<artifactId>zhifubaoPay</artifactId>
<groupId>cn.tx.spingboot</groupId>
<version>1.0-SNAPSHOT</version>
</dependency>
接下来,在测试类中实现有关方法:
package cn.tx;
import cn.tx.service.PayService;
import java.util.ServiceLoader;
public class test {
public static void main(String[] args){
//这里用到实现查找的一个工具类:java.util.ServiceLoader。
ServiceLoader<PayService> load = ServiceLoader.load(PayService.class);
for (PayService payService:load){
System.out.println(payService);
}
}
}
测试:
结果发现没有打印出来,原因:不符合spi规范。
我上面提到,当外部程序装配这个模块的时候,通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。由于我们没有创建该目录的文件,所以它找不到相关的实现类。
解决办法:在工程中的resources下创建如下文件:
然后将之前所写的接口的全路径复制,在文件下新建一个文件:
再将实现类的全路径复制到文件中:
这样再去测试就可以读取到了。