SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。
先来看看如何使用
1.定义接口
public interface HelloService {
public String serviceType();
public void sayHello(String name);
public String sayBye(String name);
}
2.实现接口
public class HelloServiceImpl implements HelloService {
@Override
public String serviceType() {
return "Hello";
}
@Override
public void sayHello(String name) {
System.out.println("name = [" + name + "]");
}
@Override
public String sayBye(String name) {
return name + " " + "bye bye";
}
}
public class HelloServiceImpl2 implements HelloService {
@Override
public String serviceType() {
return "Second";
}
@Override
public void sayHello(String name) {
System.out.println("name2 = [" + name + "]");
}
@Override
public String sayBye(String name) {
return "good night " + name;
}
}
3.在resources目录下创建META-INFO/services目录,并创建一个以接口全名的文件不带任何后缀,然后将实现类的全路径写上去
com.zdnst.boot.spi.impl.HelloServiceImpl
com.zdnst.boot.spi.impl.HelloServiceImpl2
4.通过ServiceLoader进行加载
public class SPIMain {
public static void main(String[] args){
ServiceLoader<HelloService> loader = ServiceLoader.load(HelloService.class);
Iterator<HelloService> it = loader.iterator();
while(it.hasNext()){
HelloService service = it.next();
if(service.serviceType().equals("Hello")){
service.sayHello("张三");
String name = service.sayBye("张三");
System.out.println(name);
}
}
}
}
spi虽然提供懒加载,但是不会根据选择进行加载,只要配置文件中配置了实现类都会被实例,所以需要在实现类中定义过滤规则,样例中通过定义serviceType方法来进行选择实现类。
总的来说Java SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制。系统设计的各个抽象,往往有很多不同的实现方案,在面向的对象的设计里,一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。
Java SPI就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。所以SPI的核心思想就是解耦。