1. Java SPI
1.1 概念
SPI全称是Service Provider Interface,是JDK内置的一种服务提供发现功能,一种动态替换发现的机制。
1.2 规范
使用SPI需要遵循下面的规范:
-
设置META-INF目录,并在下面新建一个services目录
-
在上面创建的目录下,放置配置文件
-
使用ServiceLoad.load()方法进行加载
1.3 示例
首先新建一个maven项目,结构如下: 1572792821854.png下面分步骤进行描述。
首先新建一个HelloService接口:
public interface HelloInterface {
public void sayHello();
}
以及该接口的两个实例:
public class ImageHello implements HelloInterface {
public void sayHello() {
System.out.println("Image hello!");
}
}
public class TextHello implements HelloInterface {
public void sayHello() {
System.out.println("Text hello");
}
}
然后在resources目录下的META-INF.services目录下新建一个配置文件,文件名要求是接口全路径名,内容是要实现的接口实现类:
com.spiexample.impl.ImageHello
com.spiexample.impl.TextHello
并且文件是UTF-8编码的。
最后在SPIMain这个启动类中进行测试:
public class SPIMain {
public static void main(String[] args){
ServiceLoader<HelloInterface> serviceLoader = ServiceLoader.load(HelloInterface.class);
if (serviceLoader != null){
for(HelloInterface helloInterface : serviceLoader){
helloInterface.sayHello();
}
}
}
}
最终输出结果为:
Image hello!
Text hello
说明已经成功创建出接口的两个实现类的对象并执行方法了。
2. Dubbo SPI
Dubbo SPI相比于Java SPI而言,做了一定的优化和改进。JDK标准的SPI会一次性实例化扩展点所有实现,如果有扩展实现则初始化很耗时,如果没有用上也加载,则浪费资源。如第一小节的例子中,HelloInterface接口有很多实现类,ServiceLoader.load方法返回的serviceLoader对象中封装了所有实现类的实例。所以ServiceLoader实现了Iterable接口,以遍历出所有的实现类。
Dubbo SPI则不然,它只加载配置文件中的类,并分成不同的种类缓存在内存中,而不会全部初始化,在性能上有更好的表现。
下面是一个例子,在第一小节的例子上通过Dubbo SPI进行改进。
首先修改接口类:
@SPI("impl")
public interface HelloInterface {
public void sayHello();
}
添加了一个注解,并且设置属性为impl,表示接口默认实现为impl。
然后再定义一个这个接口的实现类:
public class NewHello implements HelloInterface {
public void sayHello() {
System.out.println("new hello!");
}
}
然后在META-INF目录下新建一个dubbo目录,再新建一个文件,文件名也是要求是接口HelloInterface的全路径名,内容是:
impl=com.spiexample.impl.NewHello
impl即对应了接口的一个实现类。
写完这些后,最后main方法中调用如下:
HelloInterface helloInterface = ExtensionLoader.
getExtensionLoader(HelloInterface.class).
getDefaultExtension();
helloInterface.sayHello();
最终就成功打印new hello,说明调用实例的就是指定的NewHello。
网友评论