概述
SPI (Service Provider Interface)是JDK里面的扩展点发现机制。这个机制存在的动机是什么呢?服务发现。 往往我们的系统某个模块底层都有不同的实现,例如数据库的底层实现有 MySQL ,NoSQL 等,而 SPI 根据配置加载具体的实现。
SPI 在dubbo 中
具体实现
package com.alibaba.xxx;
import org.apache.dubbo.rpc.Protocol;
public class XxxProtocol implements Protocol {
// ...
}
当我们外界要使用的使用就可以使用如下配置 。
<dubbo:protocol name="xxx" />
扩展点特性
扩展点使用包装类
扩展点使用包装类对真正的实现类进行包装,例如下面 :
public class XxxProtocolWrapper implements Protocol {
Protocol impl;
public XxxProtocolWrapper(Protocol protocol) { impl = protocol; }
// 接口方法做一个操作后,再调用extension的方法
public void refer() {
//... 一些操作
impl.refer();
// ... 一些操作
}
// ...
}
扩展点自动装配
那么知道了某个扩展点需要生成一个类,那么如何生成呢?dubbo 使用了类似于spring 一样的 AOP 生成,对生成过程中的依赖都会生成,然后自动装配。
扩展点选择真正实现
由于我们使用了包装类,那么真正的实现如何引进呢?在哪里引入呢?答:通过 url 来确定真正的实现类。 例如 :
接口类如下:
public interface CarMaker {
Car makeCar(URL url);
}
public interface WheelMaker {
Wheel makeWheel(URL url);
}
CarMaker 的一个实现类:
public class RaceCarMaker implements CarMaker {
WheelMaker wheelMaker;
public setWheelMaker(WheelMaker wheelMaker) {
this.wheelMaker = wheelMaker;
}
public Car makeCar(URL url) {
// ...
Wheel wheel = wheelMaker.makeWheel(url);
// ...
return new RaceCar(wheel, ...);
}
}
当上面执行
// ... Wheel wheel = wheelMaker.makeWheel(url); // ...
时,注入的 Adaptive 实例可以提取约定 Key 来决定使用哪个 WheelMaker 实现来调用对应实现的真正的 makeWheel 方法。如提取 wheel.type, key 即 url.get("wheel.type") 来决定 WheelMake 实现。Adaptive 实例的逻辑是固定,指定提取的 URL 的 Key,即可以代理真正的实现类上,可以动态生成。
在 Dubbo 的 ExtensionLoader 的扩展点类对应的 Adaptive 实现是在加载扩展点里动态生成。指定提取的 URL 的 Key 通过 @Adaptive 注解在接口方法上提供。
总结一下几个要点 :
- 具体实现的配置信息从 URL 中得到。
- 具体实现类的激活(生成 bean)依靠注解。
- 扩展类使用wrap 包装类进行封装
参考资料
- http://dubbo.apache.org/zh-cn/docs/dev/SPI.html
