计划阅读调试下Dubbo的源码,结合官方源码分析Dubbo,自身再分析总结
本文对应的Dubbo SPI
Dubbo IOC
上一篇讲到SPI,中有一部分没有介绍,在创建扩展类的时候,需要对扩展类进行依赖注入。这里涉及到Dubbo IOC
1 | "unchecked") ( |
1 | private T injectExtension(T instance) { |
通过setter方法对扩展类依赖注入
1 |
|
这里objectFactory
类型为ExtensionFactory
,但是可以在构造函数中看出来,其寻找了ExtensionFactory对应的AdaptiveExtension。AdaptiveExtension表示自适应扩展类(下节介绍)。在Dubbo中ExtensionFactory为AdaptiveExtensionFactory
1 |
|
getSupportedExtensions
和SPI部分逻辑一致,获取所有扩展的key值
1 | public Set<String> getSupportedExtensions() { |
回到最开始,injectExtension
依赖注入部分,
1 | // 寻找此类型和此名称的扩展类 |
调用AdaptiveExtensionFactory getExtension
获取到当前所有的ExtensionFactory下对应的名字和类型的扩展类。调用set方法赋值。
Dubbo 自适应扩展类
使用
回到上一章,Dubbo SPI还支持自适应扩展类。
自适应扩展类和普通扩展类的区别在于,自适应扩展类可以根绝URL参数在运行时动态创建扩展类,而不是在启动时创建完毕。
还是参照上一节的例子
1 |
|
需要自适应扩展类的需要增加@Adaptive
注解。@Adaptive
注解支持在类和方法上,如果在类上,则不会生成代理类,如果在方法上则会生成代理类。
1 | public class TestDubbo { |
robot调用方法则根据URL后的robot=xxx获取对应的扩展类
源码分析
上一节的测试类,首先进入的是 getAdaptiveExtension方法
1 | "unchecked") ( |
上面逻辑比较简单,去缓存中获取自适应扩展,获取不到则创建
1 | "unchecked") ( |
上面这段逻辑也比较简单,获取到自适应扩展类,并反射创建实例,然后对实例进行依赖注入。
对于在类上的注解,则直接返回,否则需要创建代理类。
1 | private Class<?> createAdaptiveExtensionClass() { |
上面的逻辑即构造代理类代码,并且编译生成代理类。至此,可以看到我们目前用到了两个自适应扩展类,AdaptiveCompiler 和 AdaptiveExtensionFactory。Compiler即是AdaptiveCompiler实现的扩展。这也是Dubbo中唯二的在类上有Adaptive注解的。
上面代码的核心流程在createAdaptiveExtensionClassCode
。
首先,回到刚才的例子中,在调用
1 | robot.sayHello(URL.valueOf("test://1.1.1.1:9999?robot=bumblebee"))时 |
时,其实是调用了,Robot$Adaptive扩展类中的sayHello方法。Debug之后可以看出来
1 | package test.dubbo; |
生成的代理类即以上的内容。结合生成的代理类来看createAdaptiveExtensionClassCode
。
1 | StringBuilder codeBuilder = new StringBuilder(); |
上面代码包含两部分逻辑,首先判断是否有Adaptive注解,如果不存在则报错。之后就是构造代码的package和import语句。
1 | for (Method method : methods) { |
上面的逻辑比较复杂一些,主要是寻找URL。分为两种情况,一种是我们之前例子中的,直接包含URL的,一种是通过入参可以获取到URL的。
例如 com.alibaba.dubbo.common.URL url = arg0.getUrl();
1 | // Adaptive注解的value数组 |
上面的逻辑比较复杂,主要是对URL的处理。一种情况为:
是否有默认值:如果有的话,在最后一个参数的时候需要替换默认值
Protocol 其需要从getProtocol方法中获取参数
是否含有Invocation类型,是的话需要从MethodParameter中获取
针对上面的情况
- 以Protocol接口为例,生成代码如下:
1 |
|
1 | String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() ); |
- 以Exchanger为例
1 | // header (HeaderExchanger.NAME) |
1 | String extName = url.getParameter("exchanger", "header"); |
1 | code.append("\nString extName = ").append(getNameCode).append(";"); |
从URL中拿到扩展类的名字后,获取到相关的扩展类,执行扩展类的函数即可。
之后就是收尾,构造代码了
1 | codeBuilder.append("\npublic ").append(rt.getCanonicalName()).append(" ").append(method.getName()).append("("); |
至此,Dubbo SPI部分结束了,还是有很多细节需要多Debug才能分别出来