dubbo源码解读——SPI机制

清疚 2023-02-22 13:40 81阅读 0赞

先来了解一下java中的spi机制

jdk中的spi

SPI简介
一种策略模式,通过在META-INF/services/包下定义接口命名的文件,来决定使用哪个实现
在这里插入图片描述
调用过程

  1. public class Main {
  2. public static void main(String[] args) {
  3. System.out.println("---加载接口--");
  4. ServiceLoader<SpiService> serviceLoader = ServiceLoader.load(Log.class);
  5. Iterator<SpiService > iterator = serviceLoader.iterator();
  6. //上面只声明了两个接口,所以这里会分别调用rmi和rest的实现类的sayhello方法
  7. while (iterator.hasNext()) {
  8. SpiService s1= iterator.next();
  9. s1.sayHello
  10. }
  11. System.out.println("---运行结束---");
  12. }
  13. }

spi 全称为(Service Provider Interface),是JDK内置的一种服务提供机制。
这个是针对厂商或者插件的。
一般来说对于未知的实现或者对扩展开放的系统,通常会把一些东西抽象出来,抽象的各个模块往往有很多不同的实现方案,例如:日志模块、xml解析模块、jdbc模块等。
SPI约定
当服务的提供者,提供了服务接口的一种实现之后,在jar包中META-INF/services目录里同时创建一个以服务接口命名的文件。
该文件里就是实现该服务接口的具体实现类(全称)。
而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。

在日志实现中可以使用

dubbo中的spi实现

dubbo中的spi实现更加人性化,可以支持自定义标签中的指定属性决定实现类的读取
比如这个如在均衡的策略
在这里插入图片描述

dubbo中的api的使用流程

凡是dubbo中, 接口上有 @SPI标注的,都表明此接口支持扩展,能以标签中属性的方式进行配置扩展
类似这个负载的接口

  1. @SPI("random")
  2. public interface LoadBalance {
  3. @Adaptive({ "loadbalance"})
  4. <T> Invoker<T> select(List<Invoker<T>> var1, URL var2, Invocation var3) throws RpcException;
  5. }

下面我们自己来写一个负载的扩展类
1、引入jar依赖

  1. <!-- dubbo支持 -->
  2. <dependency>
  3. <groupId>com.alibaba</groupId>
  4. <artifactId>dubbo</artifactId>
  5. <version>2.5.7</version>
  6. <scope>provided</scope>
  7. </dependency>

2、编写实现类
在这里插入图片描述
3、resource下新建类全路径名文件,为每个实现分配一个key
在这里插入图片描述
4、在消费端,便可使用上面步骤中定的实现策略(以key指代)
在这里插入图片描述

dubbo中SPI机制源码解读

在运行的时候会通过一个叫做ExtensionLoader的加载器来进行dubbo的扩展点加载
拿消费端的Reference标签举例,
通过看dubbo解析自定义标签的源码

  1. this.registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));

我们知道dubbo是将reference标签的内容封装到ReferenceBean里的,打开ReferenceBean

  1. public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean { }

打开父类ReferenceConfig,可以看到创建代理对象时的这一句核心代码

  1. this.invoker = refprotocol.refer(this.interfaceClass, (URL)this.urls.get(0));

而这个refprotocol则是个ReferenceConfig的静态变量
(也许您会好奇,每一个reference标签对应一个ReferenceConfig对象,那在这里的协议使用类变量,会不会导致每个对象只有一个协议呢?这里就牵扯到dubbo的ExtensionLoader加载器了)

  1. //getExtensionLoader获取加载器 getAdaptiveExtension获取代理类
  2. private static final Protocol refprotocol = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

getExtensionLoader(Protocol.class)为protocol接口生成一个加载器
getAdaptiveExtension(),使用加载器生成一个代理对象—— protocol接口对象
代理对象执行时,根据参数(扩展名extName)选择实际对象

最后的效果:
每个接口扩展点——- 对应一个ExtensionLoader加载器,内部是一个currenthashmap 如:
protocol ——————— ExtensionLoader实例< protocol>
filter ——————— ExtensionLoader实例< filter >
loadbalance ——————— ExtensionLoader实例< loadbalance >

代理对象是实时生成的,包括class文件,当然只生成接口中有@Adaptive注解的方法
在这里插入图片描述

在这里插入图片描述
a、dubbo启动加载实现类时,以 key-实例 方式map缓存各个实现类
b、实际调用时,通过key —取实现需要那个实现
c、调用的发生,由生成的代理对象的来发起,最终是从URL总线中,找出extName值,
extName做为key,在缓存map中取出正确的实现实现类

发表评论

表情:
评论列表 (有 0 条评论,81人围观)

还没有评论,来说两句吧...

相关阅读

    相关 dubbo解析-SPI机制

     架构体系   框架介绍   概述   Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spri