概述
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