• 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,等了解多了再回头看看,会容易理解很多

    --------------以上皆原创,给未来的自己留下一点学习的痕迹!--------
  • 相关阅读:
    【算法笔记】多线程斐波那契数列
    RAID技术详解
    Mysql 语句汇总(性能篇)
    JS 网页打印解决方案
    MyEclipse修改
    几个需要学习的点和技术
    MyEclipse配色字体等配置的解决方案
    使用hibernate 分表做增删改查
    Web平台开发流程以及规范
    easyui使用总结
  • 原文地址:https://www.cnblogs.com/wyq1995/p/15600508.html
Copyright © 2020-2023  润新知