Hola,我是 yes。
这篇继续之前提到的 Dubbo SPI 来讲讲扩展点自适应机制(这篇文末会画个 Dubbo SPI 完整的流程图,来帮助大家理解)。
这个名词听起来好像很高级,其实就是一个扩展代理类,通过参数返回对应的扩展实现类。
我写个代码看看应该就对扩展自适应一目了然了。
![](https://i-blog.csdnimg.cn/blog_migrate/cbf8d635122ba6c319f2c1330da02f43.png)
代码中的 AdaptiveYes 就是代理类,实现同样的接口,然后根据调用时候的参数去选取对应的实现类进行调用,这就是扩展自适应。
例如拿到的yesName 是“yesA”则返回YesA这个实现类,是“yesB”则返回YesB这个实现类
是不是没什么花头?就简单加了一层,可以根据请求的参数来动态选择对应的扩展实现类,让扩展更加灵活。
理解了什么是扩展自适应之后,我们再来具体看看 Dubbo 中的实现。
Dubbo 中的 Adaptive 注解
![](https://i-blog.csdnimg.cn/blog_migrate/44decfbcee475fba7d9b7f9384a14c95.png)
从代码中可以看到 Adaptive 可以注解到类上或方法上。
注解到类上的话表明这个类就是要用的代理类,所以 Dubbo 不需要用字节码工具为这个扩展生成代理类。
注解在方法上表明 Dubbo 需要为这个方法生成代理逻辑。
拿上面提到的 AdaptiveYes 类来说,如果这个类上被标注了@Adaptive 那么说明这个类就是 Yes 这个扩展要用的代理类,框架就不用动态生成了。
![](https://i-blog.csdnimg.cn/blog_migrate/aad93a3b24e81804c7343497ffe4b3bc.png)
如果 @Adaptive 被标记在接口 Yes 的 sayHi 这个方法上,那 Dubbo 就需要用字节码工具来生成 AdaptiveYes 这个代理类。
![](https://i-blog.csdnimg.cn/blog_migrate/c8857ff793978218dbd38e3fa8cca25d.png)
在 Dubbo 中,类上被修饰 @Adaptive 只有两个,分别是AdaptiveCompiler(自适应选择编译器实现)
和 AdaptiveExtensionFactory(自适应选择扩展工厂)
还记得之前提到的 Dubbo 自动注入功能的代码嘛?就是通过 SPI 找到的扩展实现类内部需要注入对象的功能。
![](https://i-blog.csdnimg.cn/blog_migrate/66ff9bab97d4bb3ae38ccd27ff70f6f5.png)
当时留了个坑,现在填上。
这行代码是要通过扩展实现类 set 方法上的参数找到扩展点要注入的对象,而这个 objectFactory 就是自适应扩展代理类。
![](https://i-blog.csdnimg.cn/blog_migrate/bdb9b58eb67bf50cfef93fc4faf3d8d4.png)
Dubbo 中的注入相对 Spring 而言比较复杂,因为有可能需要注入的是 Dubbo 中其它自适应扩展对象,也有可能注入的是 Spring Bean,或者是我们自行定义的容器里面的对象等等。
所以依赖注入的对象需要去多处查找,因此加了一层,搞了个自适应代理扩展类。
在 Dubbo 中的 ExtensionFactory (扩展工厂,从工厂中查找要注入的对象)有三个实现:
![](https://i-blog.csdnimg.cn/blog_migrate/1928b7674aba854883302b391e0d8641.png)
SpringExtensionFactory:从 Spring 容器中去加载 Extension
SpiExtensionFactory:Dubbo 自己的SPI 去加载 Extension
AdaptiveExtensionFactory: 自适应的 AdaptiveExtensionLoader,也就是我们上面提到的代理类,由人工编写的。
ExtensionLoader 中的 objectFactory 用的就是 AdaptiveExtensionFactory 这个实现类了,咱们跑起来打个断点看看。
![](https://i-blog.csdnimg.cn/blog_migrate/5d621005c347036cf490841d8c81c934.png)
嗯,确实是,还能看到 AdaptiveExtensionFactory 的成员变量 factories 还保存了另外两个工厂。
我们来简单地看下 AdaptiveExtensionFactory 。
这个工厂会先去加载所有 ExtensionFactory 的扩展类,然后查找 Extension 的时候会遍历每个 ExtensionFactory 实现类去找要注入的对象,找到了就返回。
![](https://i-blog.csdnimg.cn/blog_migrate/f3e8ad7d6793018e399450ebb93e25b1.png)
所以 Dubbo 就是通过这种方式来实现 IOC 的注入,很粗暴简单,每个工厂遍历过去查找需要注入的对象。
好了,填了之前文章 Dubbo IOC 的坑,也讲了下 @Adaptive 修饰类的情况(就是直接把这个类作为代理类)。
接下来要讲讲修饰方法的情况,相对而言比修饰类要复杂。
不过也不难,无非就是多了几步,要用字节码工具生成代理类的源码,然后编译成 Java 字节码,然后加载到 JVM 中,就是这样。
我们来看看源码,入口就是 getAdaptiveExtension 方法。
![](https://i-blog.csdnimg.cn/blog_migrate/9cdb6f4de333158cc0fade1862e7321a.png)
那个 cachedApaptiveClass 就是 SPI 扫描对应文件夹加载类的时候记录的。
![](https://i-blog.csdnimg.cn/blog_migrate/c76e5f0873f9a1f6636dc562987f536a.png)
结合上面两个代码图就知晓为什么类上标注 @Adaptive 的时候直接就用那个类,不然就需要框架生成代理类了。
我们再来看看框架生成的代码是怎样的。
![](https://i-blog.csdnimg.cn/blog_migrate/1f832be05c4f4dead4c287c4ec0f6815.png)
我们看的是 Protocol (协议接口,Dubbo 支持很多协议,默认dubbo协议)的自适应扩展代码,我们先看下 Protocol 这个接口的定义,然后再看看生成的代码。
![](https://i-blog.csdnimg.cn/blog_migrate/75afae96c347677711ca48c2c261ce46.png)
![](https://i-blog.csdnimg.cn/blog_migrate/815acaa28a795b021d2dc31aa4155246.png)
如何生成上面 code 内容的方法我就不分析了,反正就是各种判断然后字符串拼接而成的,至于编译之前也提到了,Dubbo 默认选的是 javassist。
至此整个自适应逻辑扩展已经很清晰了,然后上完整 SPI 的图,相信看了图之后整个流程就了然于心了!
![](https://i-blog.csdnimg.cn/blog_migrate/a0fc74ed5921a475d81cae5978cfc83f.png)
Dubbo 中的 Activate
再提一提 @Activate ,这个就不进行源码分析了,此注解是用来实现自动激活特性的。
![](https://i-blog.csdnimg.cn/blog_migrate/0aca2723c49917ca25740e915df1b646.png)
主要的参数是:
group:表明类得在 Provider 端被激活还是在 Consumer 端被激活。
value:URL 参数上出现指定的值被激活。
order:扩展激活类之间的排序。
简单地说就是标注了这个注解的扩展会被记录,然后调用的时候根据参数来选取合适的扩展实现类。
比如参数的 group 和当前扩展类的 group 匹配,出现了指定的 key ,然后就会被激活。
对于 Filter 或者一些 Listener 来说比较有用,用来同时加载多个实现类,再看下官网的例子已经就比较清楚了。
![](https://i-blog.csdnimg.cn/blog_migrate/856cace1286ee3daf4a851776fec137c.png)
最后
Dubbo SPI 内容算完结了,源码分析其实不适合在公众号看,所以建议摸鱼的时候偷偷在电脑上打开看。
为了能让大家更好的理解,我已经做了标红的注释和画图了,不知道效果如何。
反正源码肯定是枯燥的,但是不管是为了深入学习还是为了应付面试,看源码这一步是一定要走的。
等 Dubbo 系列写完之后我再整理成 pdf,基本上看来下对 Dubbo 内部还是会有一定的了解的。
特别推荐一个分享架构+算法的优质内容,还没关注的小伙伴,可以长按关注一下:
长按订阅更多精彩▼
如有收获,点个在看,诚挚感谢