面试常问的dubbo的spi机制到底是什么?(上)(二)
四、dubbo的spi机制 -- ExtensionLoader源码剖析
本文是基于dubbo3.0.4版本源码剖析。
讲完了java和spring的中的spi机制,接下来进入本文的主题,dubbo的spi机制到底是什么?它与java自带的有何区别?为什么不用java的spi机制?
ExtensionLoader是dubbo的spi机制所实现的类,通过这个类来加载接口所有实现类,获取实现类的对象。同时每一个接口都会有一个自己的ExtensionLoader。
1)java的spi机制的缺点?
从我们分析java的spi机制可以看出,java约定了文件名为接口的名称,内容为实现。不知道大家有没有想过这里面有个很严重的问题,就是虽然我获取到了所有的实现类,但是无法对实现类进行分类,也就是说我无法确定到底该用哪个实现类,并且java的spi机制会一次性给所有的实现类创建对象,如果这个对象你根本不会使用,那么此时就会白白浪费资源,也就是说无法做到按需加载。
所以,dubbo就自己实现了一套spi机制,不仅解决了以上的痛点,同时也加入了更多的特性。
2)dubbo的配置文件约束。
dubbo会从四个目录读取文件META-INF/dubbo/internal/ 、META-INF/dubbo/ 、META-INF/services/、META-INF/dubbo/external/,文件名为接口的全限定名,内容为键值对,键为短名称(可以理解为spring中的对象的名称),值为实现类。
3)@SPI 注解的约束
dubbo中所有的扩展接口,都需要在接口上加@SPI注解,不然在创建ExtensionLoader的时候,会报错。代码体现在这里
顺便说说ExtensionDirector的作用,在3.0.3以前的版本,是没有这个类的,但是在之后的版本为了实现一些新的特性,就抽象出来了这个类,通过这个类来获取每个接口对应的ExtensionLoader
4)实现类的加载
先说各种特性之前,先说一下这些实现类是如何加载的,类的加载是非常重要的一个环节,与后面的spi特性有重要的关系。
类加载默认都是先调用getExtensionClasses这个方法的,当cachedClasses没有的时,才会去加载实现类,然后再把实现类放到cachedClasses中。真正实现加载的是loadExtensionClasses 方法,接下来我们详细看这个方法的源码。
checkDestroyed();
方法没什么东西,其实就是一个检查的作用。
cacheDefaultExtensionName();
缓存默认实现类的短名称。其实很简单,就是从@SPI注解中取出名称,就是默认的实现类的名称,缓存起来,ExtensionLoader有个getDefaultExtension方法,其实就是通过这个短名称对应的实现类的对象。
接下来会遍历LoadingStrategy,根据LoadingStrategy加载指定目录的文件。
我们先来看看LoadingStrategy的实例是怎么加载的。我们进入loadLoadingStrategies方法,
惊讶的发现竟然是使用了java的spi机制加载LoadingStrategy,那我们就去Classpath 路径下的 META-INF/services/路径下找这个LoadingStrategy接口的全限定名的文件,看看有哪些实现。有四个实现,也就是会按照这四个的加载策略来读取实现类。其中有个方法directory,就是指定加载的目录,这也就是我们前面说的那几个dubbo会加载的目录,其实是从这个方法返回的,你可以自己去看看这四个实现类对于这个方法的实现。其实我们也可以实现这个接口,指定我们自己想加载的目录。
这里会循环加载每个目录,我们进去loadDirectory方法。
这个其实就是拿出LoadingStrategy来调用重载的loadDirectory方法。
这里注意会调用两次loadDirectory,下面的那个其实是适配以前老版本的,不用关心。
文章转自公众号:三友的java日记