面试官:能从源码聊聊dubbo的内核吗
什么是dubbo的内核
dubbo的内核,值的是,dubbo中所有的功能,都是基于它之上完成的。dubbo的内核包括SPI,AOP,DI和Compiler。
dubbo的SPI机制和源码分析
SPI,service provider interface,服务提供者接口,就是服务发现的一种机制。
dubbo的SPI规范
dubbo的SPI机制是在JDK的SPI机制上进行的改进。
- 接口名:接口名称可以随意
- 实现类名:在接口名称前面加上一个可以表示自身功能的标识前缀。
- 提供者配置文件路径:依次查找的顺序
- META-INF/dubbo/internal
- META-INF/dubbo
- META-INF/services
- 提供者配置文件名称:接口的全限定名
- 提供者配置文件内存:文件内容为key=value形式,value为该接口实现类的全限定名,key可以随意,但是一般为前缀标识。一个类名占一行。
- 提供者加载:ExtensionLoader类,用于加载提供者配置文件中的所有类,并创建相应的实例。
Adaptive机制
扩展类的自适应机制,可以指定想要加载的扩展名,不知道就会加载默认扩展类。通过@Adaptive注解实现。而该注解可以修饰方法和类,区别很大。
- 修饰类:被该注解修饰的类称为Adaptive类,表示SPI扩展会按照类中指定的方式去加载扩展类。
- 修饰方法:如果系统中没有找到Adaptive类,找到被注解修饰的方法,则会根据Adaptive方法自动生成Adaptive类,并将其编译。
Adaptive的方法规范:
动态生成Adaptive类的格式
package <SPI 接口所在包>;
public class SPI 接口名$Adpative implements SPI 接口 {
public adaptiveMethod (arg0, arg1, ...) {
// 注意,下面的判断仅对 URL 类型,或可以获取到 URL 类型值的参数进行判断
// 例如,dubbo 的 Invoker 类型中就包含有 URL 属性
if(arg1==null) throw new IllegalArgumentException(异常信息);
if(arg1.getUrl()==null) throw new IllegalArgumentException(异常信息);
URL url = arg1.getUrl();
// 其会根据@Adaptive 注解上声明的 Key 的顺序,从 URL 获取 Value,
// 作为实际扩展类。若有默认扩展类,则获取默认扩展类名;否则获取
// 指定扩展名。
String extName = url.get 接口名() == null?默认扩展前辍名:url.get 接口名();
if(extName==null) throw new IllegalStateException(异常信息);
SPI 接口 extension = ExtensionLoader.getExtensionLoader(SPI 接口.class)
.getExtension(extName);
return extension.adaptiveMethod(arg0, arg1, ...);
}
public unAdaptiveMethod( arg0, arg1, ...) {
throw new UnsupportedOperationException(异常信息);
}
}
从上面生成的格式中知道,生成的Adpative类是用过参数URL获取知道要加载的扩展类的类名,如果没有获取到扩展名,则获取默认的扩展类。因此,对应Adpative类的规范是其参数类型是URL,或者能获取到URL类型的值。
Wrapper机制
扩展类的包装机制,就是对扩展类中SPI接口方法进行增强,进行包装,wrapper是装饰者模式的应用,一个SPI包括多个wrapper。注意,wrapper类不属于扩展类。 Wrapper类是通过Wrapper类实现的。 Wrapper定义的时候需要遵循如下规范:
- 该类中要实现SPI接口
- 该类中需要有SPI接口的引用
- 在实现的方法中要调用SPI接口对应的方法
- 正常需要以Wrapper结尾
Active机制
扩展类的激活机制,通过指定条件来激活当前的扩展类,是通过@Active注解实现的。直接加在扩展类上就可以了,用来添加一个可用于加载选择的标签。一共有5种熟悉,两种过时了,对应的意义为:
- group:为扩展类指定所属的类别。String[],表示一个扩展类可以属于多个组。
- value:为当前扩展类指定的key,是当前扩展类的一个标识。String[]类型,值就是配置文件中扩展类的扩展名。
- order:指定相同的扩展类的加载顺序,序号越小,优先级越高,默认为0。若相同,则按照注册顺序的倒序进行加载。
SPI的源码分析
我们先来理下扩展类的一个获取过程,首先要获取一个扩展类,就要获取这个扩展类的ExtensionLoader实例,而ExtensionLoader实例是需要通过ExtensionFactory创建的,而ExtensionFactory又需要ExtensionFactory的ExtensionLoader,这个ExtensionFactory的ExtensionLoader的ExtensionFactory是null。下面我们看下整个流程图,会清晰一些:

我们可以从ExtensionLoader的getExtensionLoader开始







dubbo的属性注入源码解析
上面我们已经知道了这个方法,我们继续跟进去

dubbo中AOP的源码分析
在上面的createExtension方法中,我们也看到了包装目标类,增强扩展类的入口:

dubbo中Compile机制的源码分析
dubbo中的动态编译器有两种,一种是javassitCompile,一种是JDKCompile,dubbo默认使用的是javassit。 我们先来看下使用javassit的一个过程,再进入代码。
- 首先先生成Adaptive类,类名:SPI接口名$Adaptive
- 然后获取到编译器的自适应类AdaptiveComlile
- 通过自适应类获取到默认的编译器,JavassitCompiler,并调用compiler()方法
- 首先获取到类名,然后尝试加载其class到内存,这时第一次会失败,然后会捕获异常,再通过javassit进行编译
这里我们通过获取Protocal的Adaptive类进行解析: 首先进入ExtensionLoader的getAdaptiveExtension方法





至此dubbo内核大部分的源码分析,我们就都跟了一遍了,dubbo就是通过这套内核机制,实现了高扩展的一个能力。
转载自:https://juejin.cn/post/6844904115286327310