第五章Dubbo 的内核解析

以下内容均来自,开课吧老雷,感谢老雷

第一章Dubbo概述 https://blog.csdn.net/gqs2019/article/details/115366306
第二章 Dubbo系统框架搭建 https://blog.csdn.net/gqs2019/article/details/115370178
第三章 Dubbo高级配置 https://blog.csdn.net/gqs2019/article/details/115403364
第四章 Dubbo的系统架构解析 https://blog.csdn.net/gqs2019/article/details/115456656
第五章Dubbo 的内核解析 https://blog.csdn.net/gqs2019/article/details/115457046
第六章 Dubbo的源码解析 https://blog.csdn.net/gqs2019/article/details/115460065

所谓 Dubbo 的内核是指,Dubbo 中所有功能都是基于它之上完成的,都是由它作为基
础的。dubbo 的内核包含四部分:SPI、AOP、IoC,与 Compiler。

一、JDK的SPI是什么?

  1. 简介
    在这里插入图片描述
    SPI,Service Provider Interface,服务提供者接口,是一种服务发现机制。
  2. JDK 的 SPI 规范
    JDK 的 SPI 规范规定:
    ? 接口名:可随意定义
    ? 实现类名:可随意定义
    ? 提供者配置文件路径:其查找的目录为 META-INF/services
    ? 提供者配置文件名称:接口的全限定性类名,没有扩展名。
    ? 提供者配置文件内容:该接口的所有实现类的全限类性类名写入到该文件中,一个类名
    占一行
  3. 代码举例 12-jdkspi
    (1) 定义工程
    创建一个 Maven 的 Java 工程。
    (2) 定义接口
    在这里插入图片描述
    (3) 定义两个实现类
    在这里插入图片描述
    在这里插入图片描述
    (4) 创建目录与配置文件
    在这里插入图片描述
    (5) 定义配置文件内容
    在这里插入图片描述
    (6) 定义服务消费者类
    在这里插入图片描述

二、Dubbo 的 SPI

Dubbo 并没有直接使用 JDK 的 SPI,而是在其基础之上对其进行了改进。

  1. 规范说明
Dubbo 的 SPI 规范是:
? 接口名:可以随意定义
? 实现类名:在接口名前添加一个用于表示自身功能的“标识前辍”字符串
? 提供者配置文件路径:在依次查找的目录为
META-INF/dubbo/internal
META-INF/dubbo
META-INF/services
? 提供者配置文件名称:接口的全限定性类名,无需扩展名
? 提供者配置文件内容:文件的内容为 key=value 形式,value 为该接口的实现类的全限类
性类名,key 可以随意,但一般为该实现类的“标识前辍”(首字母小写)。一个类名占
一行。
? 提供者加载:ExtensionLoader 类相当于 JDK SPI 中的 ServiceLoader 类,用于加载提供者
配置文件中所有的实现类,并创建相应的实例。
  1. Dubbo 的 SPI 举例 13-dubbospi
    下面将实现一个下单功能。其支付方式仅支持支付宝或微信两种方式。即这里要定义一
    个 SPI 接口,其存在两个扩展类。
    (1) 创建工程
    创建一个 Maven 的 Java 工程。
    (2) 导入依赖
    导入 Dubbo 的依赖。
<properties>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 <maven.compiler.source>1.8</maven.compiler.source>
 <maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
 <!--Dubbo 依赖-->
 <dependency>
 <groupId>org.apache.dubbo</groupId>
 <artifactId>dubbo</artifactId>
 <version>2.7.3</version>
  </dependency>
 <dependency>
 <groupId>junit</groupId>
 <artifactId>junit</artifactId>
 <version>4.11</version>
 <scope>test</scope>
 </dependency>
</dependencies>

(3) 定义 SPI 接口
在这里插入图片描述
(4) 定义两个扩展类
在这里插入图片描述
在这里插入图片描述
(5) 定义扩展类配置文件
在这里插入图片描述
(6) 定义测试类
在这里插入图片描述

三、Adaptive 机制

Adaptive 机制,即扩展类的自适应机制。即其可以指定想要加载的扩展名,也可以不指
定。若不指定,则直接加载默认的扩展类。即其会自动匹配,做到自适应。其是通过@Adaptive
注解实现的。一个 SPI 只能有一个 Adaptive 类。

  1. @Adaptive 注解
    @Adaptive 注解可以修饰类与方法,其作用相差很大。
    (1) @Adaptive 修饰类
    被@Adapative 修饰的 SPI 接口扩展类称为 Adaptive 类,表示该 SPI 扩展类会按照该类中
    指定的方式获取,即用于固定实现方式。其是装饰者设计模式的应用。
    (2) @Adaptive 修饰方法
    被@Adapative 修饰 SPI 接口中的方法称为 Adaptive 方法。在 SPI 扩展类中若没有找到
    Adaptive 类,但系统却发现了 Adapative 方法,就会根据 Adaptive 方法自动为该 SPI 接口动
    态生成一个 Adaptive 扩展类,并自动将其编译。例如 Protocol 接口中就包含两个 Adaptive
    方法。
  2. Adaptive 类代码举例 14-adaptiveclass
    (1) 创建工程
    复制 13-dubbospi 工程,在其基础之上修改。
    (2) 定义 adaptive 扩展类
    在这里插入图片描述
    在这里插入图片描述
    (3) 修改扩展类配置文件
    在这里插入图片描述
    (4) 测试 1
    在这里插入图片描述
    (5) 测试 2
    在这里插入图片描述
  3. Adaptive 方法规范
    下面我们准备要定义 Adaptive 方法。那么 Adaptive 方法的定义有什么要求呢?我们通
    过查看动态生成的 Adaptive 类来总结 Adaptive 方法的要求。
    (1) 动态生成 Adaptive 类格式
    package <SPI 接口所在包>;
    public class SPI 接口名$Adpative implements SPI 接口 {
     public adaptiveMethod (arg0, arg1, ...) {
     // 注意,下面的判断仅对 URL 类型,或可以获取到 URL 类型值的参数进行判断
     // 例如,dubbo 的 Invoker 类型中就包含有 URL 属性
    if(arg1==null) throw new IllegalArgumentException(异常信息)if(arg1.getUrl()==null) throw new IllegalArgumentException(异常信息);
     URL url = arg1.getUrl();
    // 其会根据@Adaptive 注解上声明的 Key 的顺序,从 URL 获取 Value,
    // 作为实际扩展类。若有默认扩展类,则获取默认扩展类名;否则获取
    // 指定扩展名名。
     String extName = url.get 接口名() == null?默认扩展前辍名:url.get 接口名();
     if(extName==null) throw new IllegalStateException(异常信息);
     SPI 接口 extension = ExtensionLoader.getExtensionLoader(SPI 接口.class)
    .getExtension(extName);
     return extension. adaptiveMethod(arg0, arg1, ...);
     }
     public unAdaptiveMethod( arg0, arg1, ...) {
     throw new UnsupportedOperationException(异常信息);
     }
    }
    

(2) 方法规范
从前面的动态生成的 Adaptive 类中的 adaptiveMethod()方法体可知,其对于要加载的扩
展名的指定方式是通过 URL 类型的方法参数指定的。所以对于 Adaptive 方法的定义规范仅
一条:其参数包含 URL 类型的参数,或参数可以获取到 URL 类型的值。方法调用者是通过
URL 传递要加载的扩展名的。
4. Adaptive 方法代码举例 14-adaptivemethod
(1) 创建工程
复制 14-adaptiveclass 工程,在其基础之上修改。
(2) 修改 SPI 接口
在这里插入图片描述
(3) 修改两个扩展类
在这里插入图片描述
在这里插入图片描述
(4) 删除原来的 Adaptive 类
若存在 Adaptive 类,即使定义了 Adaptive 方法,其执行的也是 Adaptive 类,所以这里
要首先将 Adaptive 类删除。
(5) 定义扩展类配置文件
由于 Adaptive 类已经删除,所以在配置文件中也需要将 Adaptive 类的注册也删除。
(6) 测试 1
在这里插入图片描述
(7) 测试 2

在这里插入图片描述

四、Wrapper 类规范

Wrapper 机制不是通过注解实现的,而是通过 Wrapper 类实现的。
Wrapper 类在定义时需要遵循如下规范。
? 该类要实现 SPI 接口
? 该类中要有 SPI 接口的引用
? 在接口实现方法中要调用 SPI 接口引用对象的相应方法
? 该类名称一般以 Wrapper 结尾(不是必需的)
代码举例 15-wrapper
复制 14-adaptivemethod 工程,在其基础之上修改。
(1) 定义两个 wrapper 类
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(2) 修改扩展类配置文件
在这里插入图片描述

五、Activate 机制

用于激活扩展类的。
Activate 机制,即扩展类的激活机制。通过指定的条件来激活当前的扩展类。其是通过
@Activate 注解实现的。
@Activate 注解
在@Activate 注解中共有五个属性,其中 before、after 两个属性已经过时,剩余有效属
性还有三个。它们的意义为:
? group:为扩展类指定所属的组别,是当前扩展类的一个标识。String[]类型,表示一个
扩展类可以属于多个组。
? value:为当前扩展类指定的 key,是当前扩展类的一个标识。String[]类型,表示一个扩
展类可以有多个指定的 key。
? order:指定筛选条件相同的扩展类的加载顺序。序号越小,优先级越高。默认值为 0。
order 值相同,则按照注册顺序的逆序进行加载。
代码举例
(1) 创建工程
复制 13-dubbospi 工程,在其基础之上修改。
(2) 修改扩展类 AlipayOrder
在这里插入图片描述
(3) 修改扩展类 WeChatOrder
在这里插入图片描述
(4) 定义扩展类 CardOrder
在这里插入图片描述
(5) 定义扩展类 CashOrder
在这里插入图片描述
(6) 定义扩展类 CouponOrder
在这里插入图片描述
(7) 注册扩展类
在这里插入图片描述
(8) 测试

在这里插入图片描述

六、总结

在配置文件中可能会存在四种类:普通扩展类,Adaptive 类,Wrapper 类,及添加了
@Activate 注解的扩展类。它们的共同点是,都实现了 SPI 接口。
? 一个 SPI 接口的 Adaptive 类只能有一个(无论是否是自动生成的),而 Wrapper 类与
Activate 类都可以有多个。
? 只有普通扩展类与 Activate 类属于扩展类,而 Adaptive 与 Wrapper 类均不属于扩展类
? Adaptive、Activate 都是通过注解实现的,而 Wrapper 则不是。

七、Dubbo 的 SPI 源码解析

下面以 Spring 容器的获取过程为例来解析 SPI 的执行过程。
1.总思路
在这里插入图片描述
2. 代码解析
从 ServiceConfig 入手
在这里插入图片描述
在这里插入图片描述
(1) factory 实例的 loader 的 extensionFactory 实例是 null
这个 objectFactory 实例也是通过 SPI 获取到的。
在这里插入图片描述
在这里插入图片描述
(2) 获取 factory 的 extensionLoader
构造器执行完毕,则 ExtensionFactory 的 extensionLoader 实例就创建完毕。重新返回其
调用语句。
在这里插入图片描述
在这里插入图片描述
(3) 创建 Container 的 loader 的 extensionFactory
跟踪执行 getAdaptiveExtension()。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
返回 loadExtensionClasses()方法。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
跟踪 cacheAdaptiveClass()方法。
在这里插入图片描述
跟踪 cacheWrapperClass()方法。
在这里插入图片描述
重新返回 loadClass()方法
在这里插入图片描述
跟踪 cacheActiveClass()方法
在这里插入图片描述
跟踪 cacheName()方法。
在这里插入图片描述
跟踪 saveInExtensionClass()方法。
在这里插入图片描述
可以看出,其是将当前 name 与扩展类配对后放到了 extensionClasses 中了。
(4) 获取 Protocol 的 extensionLoader
前面的代码执行完毕,然后一层层的返回,最终就返回到了这里。
在这里插入图片描述
此时 Protocol 的 ExtensionLoader 的 objectFactory 实例就创建完毕。返回其调用语句。
在这里插入图片描述
最后第 131 行返回这个新创建的 loader,返回给该 getExtensionLoader()方法的调用语句。

在这里插入图片描述

(5) 获取 Protocol 的默认扩展类实例
此时再跟踪第 121 行的 getAdaptiveExtension()方法,是通过刚才获取到的 Protocol 的
laoder 对象获取 Protocol 的自适合实例。其执行过程与前面的相同。

八、Dubbo 的 IoC 源码解析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
跟踪 getExtension()方法。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
使用 Spring 容器获取。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

九、Dubbo 的 AOP 源码解析

在这里插入图片描述
那么,它们是如何包装 instance 实例的呢?这就要分析这些 Wrapper 类了。
ProtocolFilterWrapper 解析
下面以 ProtocolFilterWrapper 为例进行解析
在这里插入图片描述
然后我们再查看 Protocol 接口中包含的方法。
在这里插入图片描述
然后再逐个查看 ProtocolFilterWrapper 类中对这些方法的重写,即对于原 Protocol 实例
的增强。
在这里插入图片描述
在这里插入图片描述

十、Dubbo 的动态编译 Compile 源码解析

Dubbo 的动态编译器有两种:JavassistCompile 与 JdkCompiler。在 Dubbo 框架中,只要
是动态编译,使用的都是默认的 JavassistCompiler,而编译器 JdkCompiler 仅是让用户可以进
行自由选择使用的。所以我们在这里只分析 JavassistCompile 编译器。
1 Javassist 简介
Javassist 是一个开源的分析、编辑和创建 Java 字节码的类库。一般情况下,对字节码文
件进行修改是需要使用虚拟机指令的。而使用 Javassist,可以直接使用 java 编码的形式,而
不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。
2 手工实现 Javassist 动态编译 17-javassist
(1) 创建工程
创建一个 Maven 的 Java 工程。
(2) 导入依赖
仅需要一个 Javassist 依赖。
在这里插入图片描述
3) 定义 JavassistCompiler

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3 解析 Dubbo 的动态编译
下面以获取 Protocol 接口的 adaptive 扩展类实例为例进行解析。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
此时查看 code 的值,其为一个字符串,其内容就是动态生成的 Adaptive 扩展类。
在其上右击,选择 copy value 即可复制该字符串的内容。
在这里插入图片描述
动态生成 Adaptive 代码后,就可以对其进行编译了。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以下内容均来自,开课吧老雷,感谢老雷
第一章Dubbo概述 https://blog.csdn.net/gqs2019/article/details/115366306
第二章 Dubbo系统框架搭建 https://blog.csdn.net/gqs2019/article/details/115370178
第三章 Dubbo高级配置 https://blog.csdn.net/gqs2019/article/details/115403364
第四章 Dubbo的系统架构解析 https://blog.csdn.net/gqs2019/article/details/115456656
第五章Dubbo 的内核解析 https://blog.csdn.net/gqs2019/article/details/115457046
第六章 Dubbo的源码解析 https://blog.csdn.net/gqs2019/article/details/115460065

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值