ExtensionLoader 是 Dubbo SPI 的核心类,里面提供了一系列的静态方法用于获取扩展类的对象。下面我们看下 getExtension()、getAdaptiveExtension()和getActiveExtension() 的实现流程。
getExtension() 流程
从上图可以看出,getExtension() 首先会调用 createExtension() 创建扩展,然后会读取该扩展的所有实现 Class 到内存中。当然这里面会判断之前是否已经加载过了 该接口的扩展Class。之后会判断这个实现类的Class 在内存中是否已经有了实例,如果之前已经new 过了对象,则直接返回即可。如果是第一次获取,则会实例化该Class,然后对该对象中的 setter 方法做自动自动注入扩展(通常是 Adaptive类型)。然后该扩展接口如果有 Wrapper 类,则会依次包装,也就是在new 出来的对象上再包上一层,最后返回这个 Wrapper 类的实例。当然对于 Wrapper 的实例也会对其 setter 方法进行自动注入相应的扩展。
getAdaptiveExtension() 流程
从上图可以看出,getAdaptiveExtension() 首先会判断缓存中是否已经初始化过Adaptive 对象,如果已经存在则直接返回。如果不存在,则调用 createAdaptiveExtension() 创建Adaptive扩展。此时和getExtension() 一样会去先加载接口的所有扩展Class到内存中,然后根据Adaptive注解生成Adaptive扩展类的代码,并编译为Class对象返回。之后会实例化该对象,并自动注入setter 属性,最后返回。
getActiveExtension() 流程
从上图可以看出,getActiveExtension() 首先会加载所有的扩展Class 到内存中,然后根据@Active 中配置的 before、after、order 进行排序。最后对于用户自定义扩展类,根据URL入参进行排序,最后返回符合条件的有序扩展类实例列表。
ExtensionFactory 实现
对于 ExtensionFactory,是Dubbo 后续版本引入的。笔者对比了 Dubbo 2.6.0 和 2.0.7 两个版本,发现这个工厂仅仅是在做 自动注入是被使用到的,而不是有些资料里面提到的 素有扩展的实例都是从该工厂中创建的。下面我们看下 2个版本的自动注入地方的代码实现。
2.0.7
2.6.0
通过上面的对比我们可以看出,ExtensionFactory 仅仅是在 setter 方法注入时,用于获取对应类型的扩展的工厂。对于Dubbo SPI 本身的扩展获取,和 ExtensionFactory 并没有什么直接关系,笔者认为其主要意义在于和 Spring Bean 的打通。下面我们介绍一下ExtensionFactory 的实现。
从 ExtensionFactory的实现可以看出,有3个实现类。其中 SpiExtensionFactory 里面实现是和 2.0.7 版本中的实现是一致的,也就是直接获取被注入类型的 Adaptive 扩展。SpringExtensionFactory 表示从 Spring 上下文中获取实例注入,其主要代码如下:
上面的代码很简单,就是直接从 Spring 上下文中获取,但是 context 是怎么注入到其中的呢。对于Bean的组装,主要实在 dubbo-config 层进行组装的,这里面会监听Spring 的初始化,然后将context注入。最后一个是 AdaptiveExtensionFactory,当然也是通常使用的一个,主要原因在于其可以自动选择使用 SPI 还是Spring 的Bean,大概代码如下:
可以看出AdaptiveExtensionFactory 是@Adaptive 标注的,因此factories 中只有 2 个工厂,且用TreeSet 存储的顺序为 SPI、Spring。也就是说注入 Bean 会优先使用 Dubbo SPI 的扩展,然后再使用Spring 的Bean。
Compiler 动态编译
Compiler 类也是 Dubbo 后续版本中引入的,动态编译主要用于生成 Adaptive 类时使用的。在老的版本中,是直接在ExtensionLoader 中写死的用 javassist 进行编译的。在新的版本中,做了抽象并做了一定的改进。其类图大致如下:
这里面和ExtensionFactory 的抽象比较类似,其中扩展主要有 Javassist 和 JDK 2种类型。这里面需要提一下的是,AbstractCompiler 这个类中的代码,首先他会拼接出全路径名,然后尝试Class.forName()加载这个类,这里是防止重复编译。如果已经加载过,此时就直接返回了。如果不存在,则调用子类方法去编译生成的code。
这里可以发现,原先的代码里并没有做防止重复编译的限制,而提取后则限制了。这是因为原先代码只在 ExtensionLoader 中使用,而其本身就会判断实例是否存在,也就是相当于防止了重复编译。这里面提取出来之后,可能会提供给更多的入口使用,因此做了限制。但是笔者看了下代码,也就ExtensionLoader 在生成 Adaptive 扩展时使用了。这从一定程度上看,这个抽象是不是有点鸡肋了。
参考:《深入理解Apache Dubbo 与实战》、Dubbo 2.6.0 源代码、Dubbo 2.0.7 源代码