dubbo分析-ExtensionLoader自适应实现

2024-03-18 07:08

本文主要是介绍dubbo分析-ExtensionLoader自适应实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

ExtensionLoader 一般使用方式

#截取 自 ServiceConfig 代码片段
public class ServiceConfig<T> extends AbstractServiceConfig {private static final long serialVersionUID = 3033787999037024738L;private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

从上面代码我们可以发现 ExtensionLoader 自适用方式,常见的 获取一个 扩展的用法。
1. getExtensionLoader 传进去的必须是一个接口。
2. 必须 在接口上添加一个 SPI 注解,表明是一个扩展。

构造方法分析

private final ExtensionFactory objectFactory;private ExtensionLoader(Class<?> type) {this.type = type;objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

上面代码 我们发现 一个新的类 ExtensionFactory 这个是干嘛的呢?ExtensionFactory 只有一个方法,就是根据 类型 和 名称得到具体的扩展实例。

首先 如果扩展type 是 ExtensionFactory objectFactory =null,否则 = 一个自适用的 ExtensionFactory。

我们具体 看下 ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension() 的 getAdaptiveExtension 方法

// 这个对象用来存储生成的自适用的实例 比如可能是Protocol$Adaptive (这个类是动态拼接生成的 )的实例
private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();public T getAdaptiveExtension() {Object instance = cachedAdaptiveInstance.get();if (instance == null) {if (createAdaptiveInstanceError == null) {synchronized (cachedAdaptiveInstance) {instance = cachedAdaptiveInstance.get();if (instance == null) {try {// 重点创建 在这里 instance = createAdaptiveExtension();cachedAdaptiveInstance.set(instance);} catch (Throwable t) {createAdaptiveInstanceError = t;throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);}}}} else {throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);}}return (T) instance;
}

我们再看 createAdaptiveExtension

private T createAdaptiveExtension() {try {//return injectExtension((T) getAdaptiveExtensionClass().newInstance());} catch (Exception e) {throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);}
}// 自适用字节码实例,比如 Protocol$Adaptive.class(动态生成) 或者 AdaptiveExtensionFactory (非动态生成)
private volatile Class<?> cachedAdaptiveClass = null;private Class<?> getAdaptiveExtensionClass() {// 重点方法,获取 tpye(扩展点接口)getExtensionClasses();if (cachedAdaptiveClass != null) {return cachedAdaptiveClass;}return cachedAdaptiveClass = createAdaptiveExtensionClass();
}//存储从扩展点配置文件读取到的 扩展点配置 key 是扩展点名称 
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();
// 获取 tpye(扩展点接口),从配置文件读取扩展点 放在 cachedClasses 缓存起来
private Map<String, Class<?>> getExtensionClasses() {Map<String, Class<?>> classes = cachedClasses.get();if (classes == null) {synchronized (cachedClasses) {classes = cachedClasses.get();if (classes == null) {// 从配置文件读取classes = loadExtensionClasses();cachedClasses.set(classes);}}}return classes;
}loadExtensionClasses 读取方法不再分析,里面主要调用 loadFile方法,主要是从dir = META-INF/dubbo/internal, META-INF/dubbo/, META-INF/services/ 依次读取配置文件,文件名都是 dir+ type.getName() 如: META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol。PS: loadFile 方法 很长,主要做了一下几点事情1.  从配置文件读取配置放到 map<String,Class<?>> 中 key 是 扩展点名称
2.  如果扩展点 类上 标注了 @Adaptive 则把他当作 自适用扩展实现 (如果有多个扩展点 类上标记了@Adaptive 会抛出异常 ) 赋值给 cachedAdaptiveClass。 这个时候不会则把他加入 到 (ConcurrentMap<Class<?>, String> cachedNames 存储 扩展点实现 到名称的映射 ) 或者下面的 cachedWrapperClasses
3. 如果扩展点类 含有一个构造方法,传入的是 拓展点接口,则把他当作包装类放到cachedWrapperClasses( private Set<Class<?>> cachedWrapperClasses;)中

Ps: @Adaptive 作用 : 如果标记一个类,则表示该类是一个自适用的扩展点实现,如。
如果标记一个拓展点接口的方法,则dubbo 会动态生成一个拓展点实现如: Protocol$Adaptive (这个类是动态拼接生成的 )

                   我调试发现 Protocol$Adaptive 的代码如下:package com.alibaba.dubbo.rpc;import com.alibaba.dubbo.common.extension.ExtensionLoader;public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {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!");}public int getDefaultPort() {throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");}public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");if (arg0.getUrl() == null)throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();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])");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);}public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {if (arg1 == null) throw new IllegalArgumentException("url == null");com.alibaba.dubbo.common.URL url = arg1;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])");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);}
}

ExtensionFactory 自适应 类

还是接着 ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension() 这个说看下 ExtensionFactory 的自适用是什么?

通过前面的分析其实已经提到过了,就是
# dubbo 默认提供的 ExtensionFactory 有如下三个
ExtensionFactory
SpringExtensionFactory (com.alibaba.dubbo.config.spring.extension)
AdaptiveExtensionFactory (com.alibaba.dubbo.common.extension.factory)
SpiExtensionFactory (com.alibaba.dubbo.common.extension.factory)

我们看下 AdaptiveExtensionFactory 的实现

// 这里 其实相当于一个包装类(所有的扩展点都是一个包装类,委托给具体的拓展点提供实现 )
// factories 就存储了具体的扩展点
// 调用 getExtension时候 就循环 factories 拿到就返回
public class AdaptiveExtensionFactory implements ExtensionFactory {private final List<ExtensionFactory> factories;public AdaptiveExtensionFactory() {ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();// loader.getSupportedExtensions() 得到全部扩展点名称 (前面说过这里不包括 adaptive扩展点实现,原因自己去看下com.alibaba.dubbo.common.extension.ExtensionLoader#loadFile 是没有把 自适用扩展点加入到 extensionClasses的)for (String name : loader.getSupportedExtensions()) {// 这里调用的是 getExtension 而不是 getAdaptiveExtension 拿到具体的扩展点list.add(loader.getExtension(name));}factories = Collections.unmodifiableList(list);}public <T> T getExtension(Class<T> type, String name) {for (ExtensionFactory factory : factories) {T extension = factory.getExtension(type, name);if (extension != null) {return extension;}}return null;}}   

获取Protocol扩展

我们分析 ServiceConfig 片段,看下它如何初始化 Protocol 的。

public class ServiceConfig<T> extends AbstractServiceConfig {private static final long serialVersionUID = 3033787999037024738L;private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

上面 的关键是创建 是getAdaptiveExtension 方法,调用堆栈如下:

 at com.alibaba.dubbo.common.extension.ExtensionLoader.createAdaptiveExtensionClass(ExtensionLoader.java:725)at com.alibaba.dubbo.common.extension.ExtensionLoader.getAdaptiveExtensionClass(ExtensionLoader.java:720)at com.alibaba.dubbo.common.extension.ExtensionLoader.createAdaptiveExtension(ExtensionLoader.java:709)at com.alibaba.dubbo.common.extension.ExtensionLoader.getAdaptiveExtension(ExtensionLoader.java:444)- locked <0x32d> (a com.alibaba.dubbo.common.utils.Holder)at com.alibaba.dubbo.config.ServiceConfig.<clinit>(ServiceConfig.java:74)at com.alibaba.dubbo.demo.learn1.ServiceExport.main(ServiceExport.java:37)

因为 Protocol 没有默认的 Adaptive 实现,所以dubbo 调用 createAdaptiveExtensionClass 方法,动态生成一个类,Protocol$Adaptive。

最终生成的 protocol 就是 Protocol$Adaptive的实例。

// Protocol$Adaptive 代码如下:
package com.alibaba.dubbo.rpc;import com.alibaba.dubbo.common.extension.ExtensionLoader;public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {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!");}public int getDefaultPort() {throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");}public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");if (arg0.getUrl() == null)throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();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])");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);}public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {if (arg1 == null) throw new IllegalArgumentException("url == null");com.alibaba.dubbo.common.URL url = arg1;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])");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);}
}

ps: 注册url
registry://192.168.41.77:2181/com.alibaba.dubbo.registry.RegistryService?application=test&dubbo=2.0.0&pid=3428&qos.port=22222&registry=zookeeper&timestamp=1523250086375

总结

目前为止 已经分析了 ExtensionLoader 的使用和 加载。ExtensionFactory 的自适用实现。以及 普通扩展点 如 Protocol$Adaptive 是怎么 生成 和使用的。

这篇关于dubbo分析-ExtensionLoader自适应实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/821551

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57