Java SPI 服务发现机制
SPI 全称是 Service Provider Interface,是java提供的一个服务发现提供机制,可以用来动态的启用和替换框架。在很多的开源框架中都使用了该机制,Dubbo中就大量的使用了该机制。
使用场景
我的理解是,它比较像java的多态,有一个定义好接口,有多个实现,根据需要,我们可以加载不同的实现类。比较常见的就是jdbc,java定义好所有的接口,各个数据库厂商实现自己的驱动
使用
我们以加载一个主题为例,定义一个主题接口,实现黑色和白色两个主题
定义好接口
1 2 3 4 5 6 7
| package com.smile;
public interface Theme {
void load();
}
|
定义多个实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.smile.impl;
import com.smile.Theme;
public class DrakTheme implements Theme {
public void load() { System.out.println("我是黑色主题"); } }
package com.smile.impl;
import com.smile.Theme;
public class WhiteTheme implements Theme {
public void load() { System.out.println("我是白色主题"); } }
|
配置文件
在项目中的resources
文件夹下创建META-INF/services
的两级目录,并添加配置主题接口的配置文件,文件的名字必须跟接口的全类限定名一致,也就是com.smile.Theme
.同时,文件的内容是两个实现类的全类限定名。如下图所示:
测试
接下来我们对两个实现类进行测试,我们需要使用ServiceLoader
来加载我们的实现类。直接看代码实现:
1 2 3 4 5 6 7
| public class ThemeTest {
public static void main(String[] args) { ServiceLoader<Theme> loader = ServiceLoader.load(Theme.class); loader.forEach(t -> t.load()); } }
|
控制台打印:
1 2 3 4
| 我是黑色主题 我是白色主题
Process finished with exit code 0
|
可以看到,程序成功的加载了主题的实现类,并成功执行。
原理
该机制的原理就是查找配置文件并使用反射进行动态的加载类文件.
通过查看源码,我们可以发现,load
方法只是设置了信息,在进行遍历实例元素时才会进行查找并进行实例化的操作。部分代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public S next() { if (!this.hasNext()) { throw new NoSuchElementException(); } else { String var1 = this.nextName; this.nextName = null; Class var2 = null;
try { var2 = Class.forName(var1, false, this.loader); } catch (ClassNotFoundException var5) { ServiceLoader.fail(this.service, "Provider " + var1 + " not found"); }
if (!this.service.isAssignableFrom(var2)) { ServiceLoader.fail(this.service, "Provider " + var1 + " not a subtype"); }
try { Object var3 = this.service.cast(var2.newInstance()); ServiceLoader.this.providers.put(var1, var3); return var3; } catch (Throwable var4) { ServiceLoader.fail(this.service, "Provider " + var1 + " could not be instantiated: " + var4, var4); throw new Error(); } } }
|
参考
Dubbo SPI