2. Dubbo原理解析-Dubbo内核实现之基于SPI思想Dubbo内核实现(转)
转载自 斩秋的专栏 http://blog.csdn.net/quhongwei_zhanqiu/article/details/41577159
SPI接口定义
定义了@SPI注解
public @interface SPI {
String value() default ""; //指定默认的扩展点
}
只有在接口打了@SPI注解的接口类才会去查找扩展点实现
会依次从这几个文件中读取扩展点
META-INF/dubbo/internal/ //dubbo内部实现的各种扩展都放在了这个目录了
META-INF/dubbo/
META-INF/services/
我们以Protocol接口为例, 接口上打上SPI注解,默认扩展点名字为dubbo
@SPI("dubbo")
public interface Protocol{
}
dubbo中内置实现了各种协议如:DubboProtocol InjvmProtocolHessianProtocol WebServiceProtocol等等

Dubbo默认rpc模块默认protocol实现DubboProtocol,key为dubbo

下面我们来细讲ExtensionLoader类
1. ExtensionLoader.getExtensionLoader(Protocol.class)
每个定义的spi的接口都会构建一个ExtensionLoader实例,存储在
ConcurrentMap<Class<?>,ExtensionLoader<?>> EXTENSION_LOADERS 这个map对象中
2. loadExtensionClasses 读取扩展点中的实现类
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
3. loadFile逐行读取com.alibaba.dubbo.rpc.Protocol文件中的内容,每行内容以key/value形式存储的。
a) 判断类实现(如:DubboProtocol)上有米有打上@Adaptive注解,如果打上了注解,将此类作为Protocol协议的设配类缓存起来,
读取下一行;否则适配类通过javasisit修改字节码生成,关于设配类功能作用后续介绍
b) 如果类实现没有打上@Adaptive, 判断实现类是否存在入参为接口的构造器(就是DubbboProtocol类是否还有入参为Protocol的构造器),
有的话作为包装类缓存到此ExtensionLoader的Set<Class<?>>集合中,这个其实是个装饰模式

c) 如果即不是设配对象也不是wrapped的对象,那就是扩展点的具体实现对象
查找实现类上有没有打上@Activate注解,有缓存到变量cachedActivates的map中
将实现类缓存到cachedClasses中,以便于使用时获取
4. 获取或者创建设配对象getAdaptiveExtension
a)如果cachedAdaptiveClass有值,说明有且仅有一个实现类打了@Adaptive, 实例化这个对象返回
b) 如果cachedAdaptiveClass为空, 创建设配类字节码。
为什么要创建设配类,一个接口多种实现,SPI机制也是如此,这是策略模式,但是我们在代码执行过程中选择哪种具体的策略呢。Dubbo采用统一数据模式com.alibaba.dubbo.common.URL(它是dubbo定义的数据模型不是jdk的类),它会穿插于系统的整个执行过程,URL中定义的协议类型字段protocol,会根据具体业务设置不同的协议。url.getProtocol()值可以是dubbo也是可以webservice, 可以是zookeeper也可以是redis。
设配类的作用是根据url.getProtocol()的值extName,去ExtensionLoader. getExtension( extName)选取具体的扩展点实现。
所以能够利用javasist生成设配类的条件
1)接口方法中必须至少有一个方法打上了@Adaptive注解
2)打上了@Adaptive注解的方法参数必须有URL类型参数或者有参数中存在getURL()方法
下面给出createAdaptiveExtensionClassCode()方法生成javasist用来生成Protocol适配类后的代码
package com.alibaba.dubbo.demo.rayhong.test;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
// 没有打上@Adaptive的方法如果被调到抛异常
public void destroy() {
throw new UnsupportedOperationException(
"method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() "
+ "of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
// 没有打上@Adaptive的方法如果被调到抛异常
public int getDefaultPort() {
throw new UnsupportedOperationException(
"method public abstractint com.alibaba.dubbo.rpc.Protocol.getDefaultPort() "
+ "of interfacecom.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
// 接口中export方法打上@Adaptive注册
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) {
if (arg0 == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invokerargument == null");
// 参数类中要有URL属性
if (arg0.getUrl() == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invokerargument getUrl() == null");
// 从入参获取统一数据模型URL
com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
// 从统一数据模型URL获取协议,协议名就是spi扩展点实现类的key
if (extName == null)
throw new IllegalStateException("Fail to getextension(com.alibaba.dubbo.rpc.Protocol) "
+ "name from url(" + url.toString() + ") usekeys([protocol])");
// 利用dubbo服务查找机制根据名称找到具体的扩展点实现
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)
ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
// 调具体扩展点的方法
return extension.export(arg0);
}
// 接口中refer方法打上@Adaptive注册
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) {
// 统一数据模型URL不能为空
if (arg1 == null)
throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
// 从统一数据模型URL获取协议,协议名就是spi扩展点实现类的key
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) "
+ "name from url(" + url.toString() + ") use keys([protocol])");
// 利用dubbo服务查找机制根据名称找到具体的扩展点实现
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)
ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
// 调具体扩展点的方法
return extension.refer(arg0, arg1);
}
}
5. 通过createAdaptiveExtensionClassCode() 生成如上的java源码代码,
要被java虚拟机加载执行必须得编译成字节码,dubbo提供两种方式去执行代码的编译:
1)利用JDK工具类编译
2)利用javassit根据源代码生成字节码。

如上图:
1)生成Adaptive代码code
2)利用dubbo的spi扩展机制获取compiler的设配类
3)编译生成的adaptive代码
在此顺便介绍下 @Adaptive注解打在实现类上跟打在接口方法上的区别
1)如果有打在接口方法上,调ExtensionLoader.getAdaptiveExtension()获取设配类,会先通过前面的过程生成java的源代码,
在通过编译器编译成class加载。但是Compiler的实现策略选择也是通过ExtensionLoader.getAdaptiveExtension(),
如果也通过编译器编译成class文件那岂不是要死循环下去了吗?
ExtensionLoader.getAdaptiveExtension(),对于有实现类上去打了注解@Adaptive的dubbo spi扩展机制,它获取设配类不在通过前面过程生成设配类java源代码,而是在读取扩展文件的时候遇到实现类打了注解@Adaptive就把这个类作为设配类缓存在ExtensionLoader中,调用是直接返回
6. 自动Wrap上扩展点的Wrap类
这是一种装饰模式的实现,在jdk的输入输出流实现中有很多这种设计,在于增强扩展点功能。这里我们拿对于Protocol接口的扩展点实现作为实例讲解。

如图Protocol继承关系ProtocolFilterWrapper, ProtocolListenerWrapper这个两个类是装饰对象用来增强其他扩展点实现的功能。ProtocolFilterWrapper功能主要是在refer 引用远程服务的中透明的设置一系列的过滤器链用来记录日志,处理超时,权限控制等等功能;ProtocolListenerWrapper在provider的exporter,unporter服务和consumer 的refer服务,destory调用时添加监听器,dubbo提供了扩展但是没有默认实现哪些监听器。
Dubbo是如何自动的给扩展点wrap上装饰对象的呢?
1)在ExtensionLoader.loadFile加载扩展点配置文件的时候
对扩展点类有接口类型为参数的构造器就是包转对象,缓存到集合中去
2)在调ExtensionLoader的createExtension(name)根据扩展点key创建扩展的时候, 先实例化扩展点的实现,
在判断时候有此扩展时候有包装类缓存,有的话利用包转器增强这个扩展点实现的功能。如下图是实现流程

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

上图代码应该不能理解,下面我们来看看ObjectFactory是如何根据类型和名字来获取对象的,ObjectFactory也是基于dubbo的spi扩展机制的

它跟Compiler接口一样设配类注解@Adaptive是打在类AdaptiveExtensionFactory上的不是通过javassist编译生成的。
AdaptiveExtensionFactory持有所有ExtensionFactory对象的集合,dubbo内部默认实现的对象工厂是SpiExtensionFactory和SrpingExtensionFactory,
他们经过TreeMap排好序的查找顺序是优先先从SpiExtensionFactory获取,如果返回空在从SpringExtensionFactory获取。
1) SpiExtensionFactory工厂获取要被注入的对象,就是要获取dubbo spi扩展的实现,
所以传入的参数类型必须是接口类型并且接口上打上了@SPI注解,返回的是一个设配类对象。

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

8. 下面 给出整体活动图

2. Dubbo原理解析-Dubbo内核实现之基于SPI思想Dubbo内核实现(转)的更多相关文章
- 【DUBBO】 Dubbo原理解析-Dubbo内核实现之基于SPI思想Dubbo内核实现
转载:http://blog.csdn.net/quhongwei_zhanqiu/article/details/41577235 SPI接口定义 定义了@SPI注解 public @interfa ...
- 3. Dubbo原理解析-Dubbo内核实现之动态编译 (转)
转载自 斩秋的专栏 http://blog.csdn.net/quhongwei_zhanqiu/article/details/41577159 我们运行的Java代码,一般都是编译之后的字节码 ...
- Java进阶专题(二十六) 将近2万字的Dubbo原理解析,彻底搞懂dubbo
前言 前面我们研究了RPC的原理,市面上有很多基于RPC思想实现的框架,比如有Dubbo.今天就从Dubbo的SPI机制.服务注册与发现源码及网络通信过程去深入剖析下Dubbo. Dubbo架构 ...
- Dubbo原理解析(非常透彻)
一.概述 dubbo是一款经典的rpc框架,用来远程调用服务的. dubbo的作用: 面向接口的远程方法调用 智能容错和负载均衡 服务自动注册和发现. 自定义序列化协议 Dubbo 架构中的核心角色有 ...
- Dubbo原理解析-注册中心之Zookeeper协议注册中心
下面我们来看下开源dubbo推荐的业界成熟的zookeeper做为注册中心, zookeeper是hadoop的一个子项目是分布式系统的可靠协调者,他提供了配置维护,名字服务,分布式同步等服务.对于z ...
- 通过dubbo暴露接口调用方法,及基于zookeeper的dubbo涉及配置文件
现在很流行的Dubbo很多朋友都听说过吧,最近我也在看这方面的东西,分享先我的心得笔记. 先说说我们团队要做的项目框架,很简单重在实现基于zookeeper的dubbo注册. 框架:springmvc ...
- Dubbo原理解析-Dubbo内核实现之SPI简单介绍
转自:https://blog.csdn.net/quhongwei_zhanqiu/article/details/41577159 Dubbo 采用微内核+插件体系,使得设计优雅,扩展性强.那所谓 ...
- 1. Dubbo原理解析-Dubbo内核实现之SPI简单介绍 (转)
转载自 斩秋的专栏 http://blog.csdn.net/quhongwei_zhanqiu/article/details/41577159 Dubbo采用 微内核 + 插件体系,使得设计优 ...
- 【DUBBO】Dubbo原理解析-Dubbo内核实现之SPI简单介绍
Dubbo采用微内核+ 插件体系,使得设计优雅,扩展性强.那所谓的微内核+插件体系是如何实现的呢!大家是否熟悉spi(service providerinterface)机制,即我们定义了服务接口标准 ...
随机推荐
- 用svg绘制圣诞帽
今天是圣诞节,无意中看到csdn博客上面给我的头像带了个圣诞帽,比较好奇,想看看csdn是怎么实现的,果然用的是svg实现,不过代码有点冗长. <html> <body> &l ...
- trajan
模板 const int N=10005; struct Edge { int v,next; }edge[5*N]; int dfn[N],low[N]; int stack[N],node[N], ...
- Content-Length mismatch, received 431737 bytes out of the expected 760836
可能原因是 composer 的安装包网址是国外镜像所致,被长城防火墙屏蔽了.可执行以下命令来解决:composer config -g repo.packagist composer https:/ ...
- 网络流24T
说出来你们可能不信,我咕了三个多星期了,今晚忽然不想再写题了,(写自闭了,把这边整理一下 1. 洛谷P2756 飞行员配对问题 二分图匹配: #include <bits/stdc++.h> ...
- 9 ArcGIS Server 性能优化
1.系统性能影响因子 地图.服务类型.数据源.客户端技术.CPU.数据结构.网络.内存.存储.部署.架构.服务接口.SDE等. 2.ArcGIS Server性能优化 数据结构与数据源:数据结构(矢量 ...
- vue的数据绑定和组件化
组件:就是自定义标签, 也是Vue的实例对象; 组件好处:就像工作分工,函数封装等 组件分为全局组件和局部组件: 全局组件,在Vue身上的组件,所有的vue挂载的元素内都可以使用:正是因为这一点,co ...
- Oracle出现高占内存的解决办法:
因为安装Oracle时,为了均衡电脑性能和数据库性能,默认内存大小为物理内存的1/8,自身内存比较大时,oracle所占的内存也会变大.而通常,我们自己的环境并不需要分配那么大的内存来支持Oracle ...
- win10下Redis安装
环境:win64 1.因为Redis官方不支持Windows,所在只能在GitHub上下载,下载地址:https://github.com/ServiceStack/redis-windows/blo ...
- css小知识 2
效果为 为什么还出现出现不同的效果? 浏览器在解析第二个p的时候,因为第二个字母见没有空格,它会认为这是一个单词没有写完,所以不会换行 列表 1.无序列表ul 第二,内部必须有子代标签<li&g ...
- js读取解析JSON类型数据【申明:来源于网络】
js读取解析JSON类型数据[申明:来源于网络] 地址:http://blog.csdn.net/sunhuaqiang1/article/details/47026841