JAVA SPI机制理解及思考
理解
Java SPI (Service Provider Interface) is the mechanism to load services dynamically. We can implement Java SPI in our application by following the specific set of rules and load the services using the ServiceLoader class.
翻译重点:Java SPI是一种动态加载服务的机制(动态调用某类中的方法),加载服务用ServiceLoader类。
实现
首先需要一个接口
import java.util.List;
/**
* 搜索接口
*
* @author anbing.zhang(anbing.zhang @ transsion.com)
* @date 2021/9/16 9:46
*/
public interface ISearch {
/**
* 根据关键字搜索
*
* @param keyword
* @return List<String>
*/
List<String> searchSth(String keyword);
}
其次,编写两个接口的实现类
import java.util.List;
/**
* 百度搜索
*
* @author anbing.zhang(anbing.zhang @ transsion.com)
* @date 2021/9/16 9:49
*/
public class BaiduSearch implements ISearch {
@Override
public List<String> searchSth(String keyword) {
System.out.println("百度结果:" + keyword);
return null;
}
}
import java.util.List;
/**
* google搜索
*
* @author anbing.zhang(anbing.zhang @ transsion.com)
* @date 2021/9/16 9:48
*/
public class GoogleSearch implements ISearch {
@Override
public List<String> searchSth(String keyword) {
System.out.println("谷歌搜索:" + keyword);
return null;
}
}
最后编写测试类
import java.util.Iterator;
import java.util.ServiceLoader;
/**
* 测试类
*
* @author anbing.zhang(anbing.zhang @ transsion.com)
* @date 2021/9/16 9:50
*/
public class Test {
public static void main(String[] args) {
ServiceLoader<ISearch> s = ServiceLoader.load(ISearch.class);
Iterator<ISearch> iterator = s.iterator();
while (iterator.hasNext()) {
ISearch search = iterator.next();
search.searchSth("hello world");
}
}
}
配置文件需要放到resources的META-INF目录下的services目录
注意目录名不能随意起。并且文件名是接口全路径,文件里的内容是需要执行的实现类全路径
transsion.test.spi.BaiduSearch
transsion.test.spi.GoogleSearch
我这里配置了两个,所以执行结果如下
百度结果:hello world
谷歌搜索:hello world
源码细节
- 为什么要指定META-INF/services?
- 如何加载配置文件中的类?
通过ServiceLoader.load(ISearch.class),定位到一个LazyIterator,ServiceLoader的hasNext和next方法都被重写过。
- 什么时候实例化的实现类?
读源码是不可能精通阅读的,看个大概就行(手动狗头)
总结
SPI好处有:
1、解耦,不同的实现不是由if-else控制,而是封装到不同的实现类
2、符合开闭原则,新增实现,无需修改原来的代码
思考一(和API的区别)
API 主要是提供方提供接口和实现,调用方直接掉提供方的接口即可。(自己调别人定义的接口,别人定义,别人实现)
SPI由调用方定义接口,提供方来实现。(自己调自己定义的接口,自己定义,别人实现)
思考二(和策略模式区别)
策略模式说简单点,就是有个接口A,两个实现类B、C,还有个封装策略的普通类D,D中有属性A,D可以通过构造方法注入A,使用D类的时候,需要哪种策略,就把B或C构造进D中就行了,调用方法时,就对应到了相应的接口实现。
从设计思想上来看,策略模式和SPI都有相似之处,都是封装变化的部分,使用时来选择。
但是也有不同的地方,SPI根据配置文件来动态定义实现类,策略模式则根据代码调整。
从应用角度来看,SPI更多应用于框架设计,策略模式偏应用开发。