作为一个Microkernel + Plugin 模式的框架, Dubbo 的所有功能点都可被用户自定义扩展所替换,那这是如何实现的呢?
SPI
SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在约定的文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。
1、JDK SPI示例
1、配置文件放在META-INF/services下
//jdk写死的
private static final String PREFIX = "META-INF/services/";
2、读取配置加载对应的类
/**
* SPI服务接口
*/
public interface Command {
public void execute();
}
public class ShutdownCommand implements Command {
public void execute() {
System.out.println("shutdown....");
}
}
public class StartCommand implements Command {
public void execute() {
System.out.println("start....");
}
}
public class SPIMain {
public static void main(String[] args) {
ServiceLoader<Command> loader = ServiceLoader.load(Command.class);
for (Cmand Cmand : loader) {
Cmand.execute();
}
}
}
运行结果
shutdown....
start....
JDK SPI 缺点
虽然ServiceLoader也算是延迟加载,但是加载是会把配置的类全部实例化,获取也只能通过遍历获取。
Dubbo SPI
Dubbo对JDK的SPI进行了扩展,类名是ExtensionLoader,配置文件采用KV(键值对)的形式,K为名称,V为实现类全限定名。
Dubbo SPI 所需的配置文件需放置在 META-INF/dubbo 路径下,配置内容如下。
optimusPrime = org.apache.spi.OptimusPrime
bumblebee = org.apache.spi.Bumblebee
下面是 Dubbo SPI 的用法(Demo来自官方文档):
public class DubboSPITest {
@Test
public void sayHello() throws Exception {
ExtensionLoader<Robot> extensionLoader =
ExtensionLoader.getExtensionLoader(Robot.class);
Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
optimusPrime.sayHello();
Robot bumblebee = extensionLoader.getExtension("bumblebee");
bumblebee.sayHello();
}
}
输出如下:
自适应拓展
有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载,Dubbo使用代理生成对应的类。Dubbo称之为自适应拓展。
应用
Dubbo作为一个开放式平台,所有拓展都可以通过 SPI 机制进行加载的,比如协议、路由、负载均衡、拦截器、注册中心等。
Reference:
1、http://dubbo.apache.org/zh-cn/docs/source_code_guide/dubbo-spi.html