SPI简介
SPI的全名为Service Provider Interface.大多数开发人员可能不熟悉,因为这个是针对厂商或者插件的。在java.util.ServiceLoader的文档里有比较详细的介绍。简单的总结下java spi机制的思想。我们系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块、jdbc模块的方案等。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。 java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。
JDK中的SPI
Java中如果想要使用SPI功能,先提供标准服务接口,然后再提供相关接口实现和调用者。这样就可以通 过SPI机制中约定好的信息进行查询相应的接口实现。
java SPI遵循如下约定:
- 当服务提供者提供了接口的一种具体实现后,在jar包的META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名;
- 接口实现类所在的jar包放在主程序的classpath中;
- 主程序通过java.util.ServiceLoader动态装载实现模块,它通过扫描该jar包META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM;
- SPI的实现类必须携带一个无参构造方法;
简单测试
创建一个maven工程,分三个模块,如下
接口:
public interface HelloService {
String sayHello();
}
实现类:
import com.rpp.service.HelloService;
public class CatHelloService implements HelloService {
public String sayHello() {
return "miao miao";
}
}
import com.rpp.service.HelloService;
public class DogHelloService implements HelloService {
public String sayHello() {
return "wang wang";
}
}
META-INF/services/com.rpp.service.HelloService 内容如下
com.rpp.service.impl.DogHelloService
com.rpp.service.impl.CatHelloService
测试类:
public class JavaSpiMain {
public static void main(String[] args) {
final ServiceLoader<HelloService> helloServices = ServiceLoader.load(HelloService.class);
for (HelloService helloService : helloServices) {
System.out.println(helloService.getClass().getName() + ":" + helloService.sayHello());
}
}
}
测试结果: