SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。
Java SPI实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制。
系统设计的各个抽象,往往有很多不同的实现方案,在面向的对象的设计里,一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。
Java SPI就是提供这样的一个机制:为某个接口寻找服务实现的机制。就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。所以SPI的核心思想就是解耦。
但是SPI也存在缺点,如:
- 不能按需加载。虽然 ServiceLoader 做了延迟加载,但是只能通过遍历的方式全部获取。如果其中某些实现类很耗时,而且你也不需要加载它,那么就形成了资源浪费
- 获取某个实现类的方式不够灵活,只能通过迭代器的形式获取
这里就来以实际的代码例子来看一看Java SPI机制的使用,首先我们来看一下项目目录,如下:
1、定义服务接口
package com.rockvine.spi.service;
public interface SpiDemoService {
void sayHello();
}
2、写出接口的一个或多个实现
package com.rockvine.spi.service.impl;
import com.rockvine.spi.service.SpiDemoService;
public class SpiDemoServiceImplA implements SpiDemoService {
@Override
public void sayHello() {
System.out.println("Hello SpiDemoServiceImplA");
}
}
package com.rockvine.spi.service.impl;
import com.rockvine.spi.service.SpiDemoService;
public class SpiDemoServiceImplB implements SpiDemoService {
@Override
public void sayHello() {
System.out.println("Hello SpiDemoServiceImplB");
}
}
package com.rockvine.spi.service.impl;
import com.rockvine.spi.service.SpiDemoService;
public class SpiDemoServiceImplC implements SpiDemoService {
@Override
public void sayHello() {
System.out.println("Hello SpiDemoServiceImplC");
}
}
3、在 src/main/resources/
下建立 /META-INF/services
目录, 新增一个以接口命名的文件
然后里面的内容是要应用的实现类,这里我们一共有3个 SpiDemoService 接口的实现类,这里只配置其中的两个实现类,如下:
4、使用 ServiceLoader 来加载配置文件中指定的实现
package com.rockvine.spi;
import com.rockvine.spi.service.SpiDemoService;
import java.util.Iterator;
import java.util.ServiceLoader;
public class SpiApp {
public static void main(String[] args) {
ServiceLoader<SpiDemoService> services = ServiceLoader.load(SpiDemoService.class);
Iterator<SpiDemoService> iterator = services.iterator();
while (iterator.hasNext()){
SpiDemoService service = iterator.next();
service.sayHello();
}
}
}