Dubbo

八、Dubbo框架源码分析:dubbo扩展机制实现

2020-10-27  本文已影响0人  还算年轻

一、SPI机制:

SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。SPI 机制在第三方框架中也有所应用,比如 Dubbo 就是通过 SPI 机制加载所有的组件。不过,Dubbo 并未使用 Java 原生的 SPI 机制,而是对其进行了增强,使其能够更好的满足需求。在 Dubbo 中,SPI 是一个非常重要的模块。基于 SPI,我们可以很容易的对 Dubbo 进行拓展。如果大家想要学习 Dubbo 的源码,SPI 机制务必弄懂。

package com.example;

public interface Spi {

    boolean isSupport(String name);

    String sayHello();

}

ServiceLoader 会遍历所有 jar 查找 META-INF/services/com.example.Spi 文件

A 厂商提供实现

package com.a.example;

public class SpiAImpl implements Spi {

    public boolean isSupport(String name) {

    return "SPIA".equalsIgnoreCase(name.trim());

}

public String syaHello() {

    return “hello 我是厂商 A”;

}

A 厂商提供的 jar 包中的 META-INF/services/com.example.Spi 文件内容为:

com.a.example.SpiAImpl #厂商 A 的 spi 实现全路径类名

B 厂商提供实现

package com.b.example;

public class SpiBImpl implements Spi {

    public boolean isSupport(String name) {

    return "SPIB".equalsIgnoreCase(name.trim());

public String syaHello() {

    return “hello 我是厂商 B”;

}

B 厂商提供的 jar 包中的 META-INF/services/com.example.Spi 文件内容为:

com.b.example.SpiBImpl #厂商 B 的 spi 实现全路径类名

ServiceLoader.load(Spi.class) 读 取 厂 商 A 、 B 提 供 jar 包 中 的 文 件 ,ServiceLoader 实现了 Iterable 接口可通过 while for 循环语句遍历出所有实现。

一个接口多种实现,就如策略模式一样提供了策略的实现,但是没有提供策略的选择, 使用方可以根据 isSupport 方法根据业务传入厂商名来选择具体的厂商。

public class SpiFactory {

    //读取配置获取所有实现

    private static ServiceLoader spiLoader = ServiceLoader.load(Spi.class);

    //根据名字选取对应实现

    public static Spi getSpi(String name) {

        for (Spi spi : spiLoader) {

            if (spi.isSupport(name) ) {

                return spi;

            }

        }

    return null;

}}

二、dubbo实现的SPI机制:

1.标记哪些接口支持SPI 机制:Dubbo 提供一个注解 ,通过该注解标记的接口支持SPI功能。

2.SPI接口实现类的信息从哪里读取?

3.SPI接口实现类加载流程:

1、首选获取SPI接口的ExtensionLoader 类,每个接口会缓存ExtensionLoader 类,ExtensionLoader 类相当于ServiceLoader 

2.通过ExtensionLoader 类加载接口的实现类

a) 先读取 SPI 注解的 value 值,有值作为默认扩展实现的 key

b) 依次读取路径的文件

    META-INF/dubbo/internal/ com.alibaba.dubbo.rpc.Protocol

    META-INF/dubbo/ com.alibaba.dubbo.rpc.Protocol

    META-INF/services/ com.alibaba.dubbo.rpc.Protocol

逐行读取对应的实现类并通过反射构造实现类。

4.SPI接口实现类加载好了,n个实现类用那个?

dubbo 提供SPI 接口的决策类:适配器类。

为什么要创建设配类,一个接口多种实现, SPI 机制也是如此,这是策略模式,但是我们在代码执行过程中选择哪种具体的策略呢。 Dubbo 采用统一数据模式 com.alibaba.dubbo.common.URL( 它是 dubbo 定义的数据模型不是 jdk 的类),它会穿插于系统的整个执行过程, URL 中定义的协 议 类 型 字 段 protocol , 会 根 据 具 体 业 务 设 置 不 同 的 协 议 。url.getProtocol()值可以是 dubbo 也是可以 webservice, 可以是zookeeper 也可以是 redis。

dubbo有两种适配类的实现方式:

1、静态@Adaptive

2、动态生成适配器代码:

获取对应的策略适配器类:

1) 生成 Adaptive 代码 code

2) 利用 dubbo 的 spi 扩展机制获取 compiler 的设配类

3) 编译生成的 adaptive 代码

策略适配器类决策逻辑:更加URL 信息

SPI实现类依赖其他类怎么办?IOC

IOC 大 家 所 熟 知 的 ioc 是 spring 的 三 大 基 础 功 能 之 一 , dubbo 的ExtensionLoader 在加载扩展实现的时候内部实现了个简单的 ioc 机制来实现对扩展实现所依赖的参数的注入, dubbo 对扩展实现中公有的 set 方法且入参个数为一个的方法,尝试从对象工厂 ObjectFactory 获取值注入到扩展点实现中去。

1、Dubbo的容器工厂是怎样的?

dubbo 的容器工厂也是采用SPI 支持扩展。

它 跟 Compiler 接 口 一 样 设 配 类 注 解 @Adaptive 是 打 在 类AdaptiveExtensionFactory 上的不是通过 javassist 编译生成的。AdaptiveExtensionFactory 持有所有 ExtensionFactory 对象的集合, dubbo内 部 默 认 实 现 的 对 象 工 厂 是SpiExtensionFactory 和SpringExtensionFactory,他们经过 TreeMap 排好序的查找顺序是优先先从SpiExtensionFactory 获取,如果返回空在从 SpringExtensionFactory 获取。

工厂适配器类:

静态适配器类查找容器的SPI实现放入List<ExtensionFactory> factories

1) SpiExtensionFactory 工厂获取要被注入的对象,就是要获取 dubbo spi扩展的实现,所以传入的参数类型必须是接口类型并且接口上打上了@SPI 注解,返回的是一个设配类对象。

2) SpringExtensionFactory, Dubbo 利用 spring 的扩展机制跟 spring 做了很好的融合。在发布或者去引用一个服务的时候,会把 spring 的容器添加到 SpringExtensionFactory 工厂集合中去, 当 SpiExtensionFactory没有获取到对象的时候会遍历SpringExtensionFactory 中的 spring 容器来获取要注入的对象

上一篇下一篇

猜你喜欢

热点阅读