今天看了下 dubbo 的 SPI 实现,收益匪浅,这里总结一下:
1. 优点:相对于 JDK 原生的 SPI 机制
- 按需加载,减少不必要的初始化
- 扩展点加载失败,相比 JDK,可以准确报出错误信息
- 扩展点可以 IOC 以及 AOP
2. 实现方案
那么 dubbo 是怎么做到以上三点的呢?
1. 按需加载逻辑
以 Transporter 为例,在 getAdaptiveExtension()时才会进行初始化动作,而并非在容器启动时加载所有的扩展类:
2. 扩展点加载失败异常报告
在加载失败时会将具体扩展点信息打印出来,即便是循环依赖产生问题,也会准确报出错误信息。
3. 扩展点可以 IOC 以及 AOP
首先看下 AOP 的实现,dubbo 在 AOP 上并没有通过字节码修改的方式,而是通过装饰器设计模式来实现的。ExtensionLoader 在加载扩展点时,如果加载到的扩展点有拷贝构造函数,则判定为扩展点 Wrapper 类,而 Wrapper 类并不是真正的实现类,如下图所示:
Wrapper 类可以有多个,并且会在所有的扩展点上添加逻辑。我们看下怎么实现的:
在创建扩展点时,进行层层装饰,以实现 AOP 的逻辑,设计非常精妙。
我们再来看看 IOC,dubbo 的 IOC,是通过 set 方法进行注入,如下图:
如果有多个扩展类可以注入,dubbo 会以如下优先级进行选择注入:
1. 有 @Adaptive 注解的扩展实现类。
在扫描配置文件时,如果发现有 Adaptive 注解的类,则立即加入缓存,以该实现类优先返回。根据代码注意到,同一个扩展点,只能有一个默认实现。
扫描配置文件完毕后,如果已经发现找到 Adaptive 注解的类,则返回,代码如下:
2. URL 中定义的扩展点。
如果未发现有 @Adaptive 注解的类,dubbo 会动态创建扩展点的实现类,以实现 Adaptive 注解的方法。这里注意到,SPI 接口至少有一个方法需要 @Adaptive 注解,否则报错,如下图所示:
createAdaptiveExtensionClassCode 是动态生成 Java 代码的部分,以下截图提取了最关键了部分。可以看看出,如果 URL 中存在定义的扩展点名称,则根据该名称获取对应的扩展实现类。
3. @SPI 注解中的 value 定义的实现。
从上图可知,如果 URL 中没有定义,则采用 SPI 的默认扩展名来获取扩展实现类。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于