从 Dubbo 内核聊聊双亲委派机制
前言
谈到 Dubbo 总是避不开 SPI 思想, 因为这个是 Dubbo 内核中非常重要的一部分, 但是 SPI 是个很大的话题, 本篇和之前的 dubbo 源码解析 - 简单原理, 与 spring 融合 https://mp.weixin.qq.com/s/eFMjlv1Drm9OZ4hX3JsSTA 一样, 为 Dubbo 源码解析专题的知识预热篇. 我们公司实际项目就用到了 Dubbo 的 SPI. 后面会给大家分享, 我们实际项目中, 是如何使用 SPI, 以及 SPI 后续我们又是如何进优化的.
插播面试题
你是否了解 spi, 讲一讲什么是 spi, 为什么要使用 spi?
对类加载机制了解吗, 说一下什么是双亲委托模式, 他有什么弊端, 这个弊端有没有什么我们熟悉的案例, 解决这个弊端的原理又是怎么样的?
spi 的简单介绍
如果提到 API 相信大家都知道, spi 的话, 知道的人就相对少一些.
简单的说, API 是给使用者使用的, spi 是给拓展者使用的. 一个好的开源框架, 必须要留一些拓展点. 让参与者尽量黑盒拓展, 而不是白盒修改代码, 否则分支, 质量, 合并, 冲突都会很难管理. 并且框架作者能做到的功能, 拓展者也一定能做到.
如果从使用层面来说, 就是运行时, 动态给接口添加实现类. 其实这有有点像 IoC 的思想, 将装配的控制权移到程序之外
如果从生活中的例子讲, 就是比如浏览器插件, 比如墙上的插头不够我们就接个排插, 而不是伤筋动骨改插头(感觉不是很贴切, 前期你暂且这么不规范的粗略理解)
再多的言语都是抽象的, 那么我们用代码来简单实现一下 spi
spi 的简单实现
接口和具体实现类
- public interface ISayName {
- void say();
- }
- public class SayEnglishName implements ISayName{
- @Override
- public void say() {
- System.out.println("Toby");
- }
- }
- public class SayChineseName implements ISayName{
- @Override
- public void say() {
- System.out.println("肥朝");
- }
- }
配置文件, 需放置在 META-INF/services / 接口全限定名
- com.toby.spi.impl.SayChineseName
- com.toby.spi.impl.SayEnglishName
demo 目录结构
测试结果如下
通过改变配置文件, 我们就能动态的改变一个接口的实现类.
细心的小伙伴可能发现, 比如我想新增一个实现类 SayFranceNameImpl, 这样的话光改配置文件也还是不行, 还要预先包里面就有这个实现类才行啊.
这个先别急, 后面会介绍 javassist, 也就是动态字节码技术. 这样可以在运行时动态生成 Java 类, 就不存在要预先把接口的实现类先在包里放好. 更多内容, 关注肥朝即可.
当然细心的小伙伴可能还发现了, 这个我就算不用 spi, 我用 spring 的 IoC 也能通过配置文件动态的注入不同的实现类啊
比如 dubbo 的设计中, 就不想强依赖 Spring 的 IoC 容器, 但是自已造一个小的 IoC 容器, 也觉得有点过度设计. 另外 dubbo 是不需要依赖任何第三方库的, 引用官方文档原话如下
理论上 Dubbo 可以只依赖 JDK, 不依赖于任何三方库运行, 只需配置使用 JDK 相关实现策略
敲黑板划重点
经常看到有人问两类问题
java 人这么多, 是否饱和了?
为什么总是要面试造火箭, 进去拧螺丝?
你可以问一下你同事, 你知道什么是 spi 吗, 如果他不知道的话, 那你觉得他把上面的这个简单的例子实现要多久? 如果从使用这个层面做区分的话, 很难做到有效的区分. 无论是做什么, 要想在竞争中脱颖而出, 就必须做到三个字. 差异化.
Java 基础中比较容易产生差异化的两个区域就在于 JVM 和并发编程. 如果只是停留在使用层面, 那么关注肥朝的博客意义并不大, 因此, 本篇的 spi 还需要与 ClassLoader 结合.
- Bootstrap ClassLoader(启动类加载器)
- Extension ClassLoader(扩展类加载器)
- App ClassLoader(应用类加载器, 也称为系统类加载器)
- public ClassLoader getContextClassLoader()// 获取线程中的上下文加载器
- public void setContextClassLoader(ClassLoader cl)// 设置线程中的上下文加载器
来源: https://juejin.im/post/5c959901e51d456d8054c635