由浅入深理解Dubbo的SPI机制

前言

在分析dubbo源码的过程当中,发现dubbo对于扩展点的加载实现的是很是巧妙的,能够达到用时才动态实例化对象,灵活且节约资源。其实Dubbo 的扩展点加载是从 JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制增强而来。它优化了JDK必须一次性实例化扩展点全部实现的缺点。java

JDK标准的SPI

一个接口能够有多个不一样的实现类,可是在一些业务场景里面,咱们须要根据不一样的业务类型去选择具体的可以知足我当前需求的实现类。大多数时候,咱们都是在内存里面维护一个Map,这样能够很高效的实现我们的目的。可是这样扩展性太差了,每次增长一种实现类,都得去修改原来的代码,风险太大了。因而,JDK给我们提供了SPI技术,用来去解决这一块的短板。具体的操做方式以下:git

要素一:接口类

package github.com.crazyStrongboy.inter;
github

要素二:不一样的实现类

package github.com.crazyStrongboy.jdk_api;
api


要素三:配置文件

在resources资源文件夹下面创建目录META-INF/services,创建接口全路径的文件,例如:
缓存

调用方式

这里drivers中会加载到我们默认给的两个实现类,若是要增长一个实现,我们只要新建一个模块,在配置文件中加上我们的实现github.com.crazyStrongboy.jdk_api.xxx便可,不会入侵原来的老代码。可是这种实现的弊端也很明显,一次性加载出来了全部的扩展类,浪费资源。相关代码在github中。app


自定义SPI实现

本身去定义一个SPI的实现,主要分三步走:ide

  1. 利用ClassLoader类加载器去读取资源文件夹指定名称的文件。
  2. 解析文件内容,并存放到一个Map容器中。键为name,值为具体实现类的Class。
  3. 封装一个对外提供的方法,参数为name值。
第一步:读取资源
第二步:解析配置文件内容
第三步:提供方法

我这边用的路径为META-INF/mars_jun/
优化


配置文件:
调用方式

其实这个不算太完整,能够把每次初始化后的对象根据相应的name存储到另外一个Map中,这样就不会每次调用getExtension都会去生成一个新的实例。可是在上面这段代码当中,我们能够观察到,我并无在一开始就将全部的扩展类都初始化出来,而是先保存扩展类的Class到Map中,直到我们须要使用的时候再去初始化实例对象。解决了JDK中SPI会一次性实例化扩展点全部实现的这个缺陷。相关代码在github中。spa


Dubbo的SPI机制

我们直接从下面这段代码开始:code

1    private static final Protocol protocol = ExtensionLoader.
2            getExtensionLoader(Protocol.class).getAdaptiveExtension();
复制代码

首先先普及下两个注解@Adaptive与@SPI

  1. 一个接口的实现类至多只能有一个被@Adaptive注解,在方法上不限,注解在类上意思是标记该类为默认扩展类,标记在方法上则可支持动态的建立扩展器。
  2. @SPI可指定默认动态生成的扩展类。

ExtensionLoader.getExtensionLoader(Protocol.class)这一句代码只是简单的构建了一个ExtensionLoader扩展器加载器对象,代码不太复杂。后面的getAdaptiveExtension才是重点。顺着链路调用,会到下图所示的方法:

关注上面圈红的标记处,主要分为了两步走:

第一步:加载全部配置文件

是否是感受似曾相识~,这一块代码也就读取了dubbo指定的几个资源目录的配置,包括"META-INF/services/" "META-INF/dubbo/" "META-INF/dubbo/internal/"这三个目录,而后一个个解析出来,丢到指定的Map集合中,缓存起来供后期相似的操做使用。固然其中还包括一些注解的解析,是不是包装类等等一些操做,这些你们能够自行点进去了解。

第二步:动态编译Protocol$Adaptive文件

在没有指定自适应的cachedAdaptiveClass的状况下(也就是实现类没有一个上面有@Adaptive注解),会调用createAdaptiveExtensionClass方法生成一个xx$Adaptive 对象。

Dubbo的SPI机制的核心点也在这里,重点关注xx$Adaptive 对象Protocol对应的是Protocol$Adaptive。我们简单看一下它生成的代码段:

它能够根据URL中的protocol字段的值去动态获取相应的扩展类,例如"dubbo"对应DubboProtocol,"registry"对应RegistryProtocol,这样是否是更加的灵活~。

这一块虽然写的很少,但核心思想点也就差很少都在这一块,顺着上面的思路一步步往下读,这个东西应该不难理解~


END

相关文章
相关标签/搜索