dubbo源码分析3(dubbo中的spi机制)

  上一篇我们看过了jdk中的spi机制,也分析了它的缺点就是会一次性将META-INF/services下的配置文件中,对应接口的全部实现类都给加载;

  而dubbo中的spi肯定是提高了性能,还扩展了原生的spi(这就是一句废话,如果没有提高性能和没有扩展新的功能,干嘛不用原生的啊(-_-メ))

1. 基于dubbo的spi栗子

  我们先说说提高性能,一般提高性能肯定就是使用到了缓存嘛,没有什么比缓存更能提高性能的了;

  啥也不管,先举个例子看一看,在上一篇的基础上,导入dubbo依赖

  <!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubbo -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.7.5</version>
        </dependency>

 

  目录结构如下:

 

  一个接口和两个实现类:

@SPI
public interface ISayNameDubbo {
    void say();
}



public class SayChineseNameDubbo implements ISayNameDubbo {
    @Override
    public void say() {
        System.out.println("dubbo 中文:哈喽,你好帅呀๑乛◡乛๑");
    }
}



public class SayEnglishNameDubbo implements ISayNameDubbo {
    @Override
    public void say() {
        System.out.println("Dubbo English:hello cool java boy");
    }
}

 

  配置文件:

 

  执行结果:

 

  使用方法跟java原生的spi几乎一样,只不过有两个地方需要注意:

  1.接口要加上@SPI注解

  2.配置文件是放在META-INF/dubbo下面,而且文件内容是以键值对的形式

 

 2. dubbo中spi机制分析

  根据上面的例子, 我们知道首先使用ExtensionLoader.getExtensionLoader(ISayNameDubbo.class)获取到指定接口的ExtensionLoader,我们看看这个ExtensionLoader是个啥?╮(╯_╰)╭

  下图中缓存的操作,可以看到ExtensionLoader其实就是对我们接口的封装,然后调用这个封装类的方法,获取该接口我们指定的实例

  

 

  先看看校验是否有SPI注解的方法:withExtensionAnnotation  

  下图所示,其实就是做了一个判断,后面我们会再看看这个注解中的值,还有啥所用

 

  接下来我们就看一下ExtensionLoader的getExtension方法,就能大概知道是怎么获取指定的实例的了;

  其实也可以根据之前使用的java的SPI机制猜一下,大概的思路就是先去META-INF目录下找到指定的配置文件,然后根据getExtension(String key)方法传入的key去找到对应的实现类的全路径,最后使用反射进行实例化就完成了

  然后我们看看代码是不是这样实现的

 

  首先我们看看getOrCreateHolder方法,其实使用了一个缓存,用于缓存key->Holer, 至于Holder的实例作用,你点开看看就知道它里面有个volatile关键字,很明显这里是为了协助我们使用单例模式的,保证一个SPI修饰的接口的实现类是单例的;

  注意,下图这种缓存的操作后面能看到很多(所以说dubbo中的SPI提升了性能๑乛◡乛๑)

 

  我们再接着看createExtension方法,我们重点看看getExtensionClasses()方法

 

 

 

  下面就是加载那三个路径下,实现类的配置文件,以key->value的形式保存起来

 

 

 

 

  如果想继续看看怎么解析配置文件的,可以看看loadDirectory()方法:

 

 

 

3.总结一下

  其实还有很多东西没说,比如dubbo中的ioc和aop,其实能说很多的,但是我这里就是不要说了,先不要太深入了;

  总结一下,dubbo的SPI的逻辑几乎都在ExtensionLoader中,而且一个@SPI修饰的接口,对应这一个ExtensionLoader实例,我们再根据配置文件中的key,调用ExtensionLoader实例的getExtension方法,这里会使用缓存和Holder来控制实例化,使得每个接口的实现类只会被实例化一次;

  在实例化的时候调用createExtension()方法,这里会首先加载"META-INF/services/","META-INF/dubbo/","META-INF/dubbo/internal/"三个目录中的所有配置文件,解析出来所有的key->value,并且将value使用Class.forName方法转为Class实例,保存到缓存中,并且根据返回我们需要的Class对象,使用反射实例化对象;

  此时对应的接口的实例化对象已经好了,但是对象里面的属性值还是空的,所以还需要使用dubbo的IOC机制,有兴趣的可以看看injectExtension方法,objectFactory就是IOC对象(类似spring的IOC容器,可以提供很多的对象),并且还用了AOP的机制,这句代码:instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));

  不过我的建议是暂时先不要管IOC和AOP,等了解多了再回头看看,会容易理解很多

posted @ 2021-11-27 21:19  java小新人  阅读(321)  评论(0编辑  收藏  举报