Dubbo扩展点加载机制

        加载机制中已经存在的一些关键注解,如@SPI、©Adaptive> ©Activateo然后介绍整个加载机制中最核心的ExtensionLoader的工作流程及实现原理。最后介绍扩展中使用的类动态编译的实 
现原理。

Java SPI

Java 5 中的服务提供商icon-default.png?t=O83Ahttps://docs.oracle.com/javase/1.5.0/docs/guide/jar/jar.html#Service%20Provider

SPI 插件扩展点使用手册

https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/spi/

JDK标准的SPI会一次性实例化扩展点所有实现,如果有扩展实现则初始化很耗时,如果没 
用上也加载,则浪费资源。

如果扩展加载失败,则连扩展的名称都蕤取不到了。比如JDK标准的ScriptEngine,通过 
getName ()获取脚本类型的名称,如果RubyScriptEngine因为所依赖的j ruby .jar不存在,导致 
RubyScriptEngine类加载失败,这个失败原因被“吃掉” 了,和Ruby对应不起来,当用户 
执行Ruby脚本时,会报不支持Ruby,而不是真正失败的原因。


增加了对扩展IoC和AOP的支持,一个扩展可以直接setter注入其他扩展。在Java SPI的使 
用示例章节(代码清单4-1 )中已经看到,java.util.ServiceLoader会一次把Printservice 
接口下的所有实现类全部初始化,用户直接调用即可oDubbo SPI只是加载配置文件中的类, 
并分成不同的种类缓存在内存中,而不会立即全部初始化,在性能上有更好的表现。

 ProtocolFilterWrapper 是 Dubbo 框架中的一个核心类,用于在服务提供者和消费者之间添加过滤器链。ProtocolFilterWrapper 通过 @Activate 注解来确定哪些过滤器适用于当前的 URL。以下是 ProtocolFilterWrapper 确定过滤器适用当前 URL 的详细过程:
1. ProtocolFilterWrapper 类
ProtocolFilterWrapper 是一个装饰器模式的实现,它包装了一个 Protocol 实例,并在其上添加了过滤器链。以下是 ProtocolFilterWrapper 的主要逻辑:

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.rpc.Exporter;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Protocol;public class ProtocolFilterWrapper implements Protocol {private final Protocol protocol;public ProtocolFilterWrapper(Protocol protocol) {this.protocol = protocol;}@Overridepublic int getDefaultPort() {return protocol.getDefaultPort();}@Overridepublic Exporter<T> export(Exporter<T> exporter) {return new InvokerDelegator<>(wrapInvoker(exporter.getInvoker()), exporter);}@Overridepublic <T> Invoker<T> refer(Class<T> type, URL url) {return wrapInvoker(protocol.refer(type, url));}private <T> Invoker<T> wrapInvoker(Invoker<T> invoker) {URL url = invoker.getUrl();// 获取所有激活的过滤器List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(url, Constants.KEYS, Constants.DEFAULT_KEY);if (filters.size() == 0) {return invoker;}return new FilterChainWrapper(invoker, filters);}

 SPI 提供商可以调用 ExtensionLoader.getActivateExtension(URL, String, String) 以查找具有给定条件的所有已激活的扩展。而getActivateExtension 会间接调用 com.alibaba.dubbo.common. extension.ExtensionLoader#loadExtensionClasses

其中 Type 是 由 ExtensionLoader.getExtensionLoader(Filter.class),决定为 Filter.

L565 - 567 就是解析 Filter 的接口上@SPI注解信息.(Filter.class 也可以替换成其他的类性

com.alibaba.dubbo.common.extension.ExtensionLoader#loadDirectory 会间接调用 com.alibaba.dubbo.common.extension.ExtensionLoader#loadClass

在此方法中会解析注解@Adaptive、@Activate

    /*** @param extensionClasses ExtensionLoader#cachedClasses 成员变量* @param resourceURL* @param clazz ExtensionLoader#loadResource 中 加载 Class.forName(  类全限定名 )* @param name  ExtensionLoader#loadResource 中 在配置文件中设置的别名*              上两参数请参考 com.alibaba.dubbo.common.extension.SPI* @throws NoSuchMethodException*/private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {if (!type.isAssignableFrom(clazz)) {throw new IllegalStateException("Error when load extension class(interface: " +type + ", class line: " + clazz.getName() + "), class "+ clazz.getName() + "is not subtype of interface.");}if (clazz.isAnnotationPresent(Adaptive.class)) {// 由于调用者 ExtensionLoader.loadResource 循环调用了 loadClass ,如果类上标注了 @Adaptive 注解,则该类为 Adaptive 类,并且只能有一个if (cachedAdaptiveClass == null) {cachedAdaptiveClass = clazz;} else if (!cachedAdaptiveClass.equals(clazz)) {throw new IllegalStateException("More than 1 adaptive class found: "+ cachedAdaptiveClass.getClass().getName()+ ", " + clazz.getClass().getName());}} else if (isWrapperClass(clazz)) {// 判断为 包装类,则维护到 ExtensionLoader.cachedWrapperClassesSet<Class<?>> wrappers = cachedWrapperClasses;if (wrappers == null) {cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();wrappers = cachedWrapperClasses;}wrappers.add(clazz);} else {clazz.getConstructor();if (name == null || name.length() == 0) {// 如果没有名字则尝试扫描 @Extension 注解, Extension 注解已经弃用了name = findAnnotationName(clazz);if (name.length() == 0) {throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);}}// 将首个类上@Activate 信息维护到 ExtensionLoader.cachedActivates 中// 将 别名 维护到 ExtensionLoader.cachedNames 中// 将 别名&类 维护到 ExtensionLoader#cachedClasses 中String[] names = NAME_SEPARATOR.split(name);if (names != null && names.length > 0) {Activate activate = clazz.getAnnotation(Activate.class);if (activate != null) {cachedActivates.put(names[0], activate);}for (String n : names) {if (!cachedNames.containsKey(clazz)) {cachedNames.put(clazz, n);}Class<?> c = extensionClasses.get(n);if (c == null) {extensionClasses.put(n, clazz);} else if (c != clazz) {throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());}}}}}

工作流程

  1. 框架读取SPI对应路径下的配置文件,并根据配置加载所有扩展类并缓存(不初始化)。
  2. 根据传入的名称初始化对应的扩展类。
  3. 尝试查找符合条件的包装类:包含扩展点的setter方法,
  4. 返回对应的扩展类实例。

        getAdaptiveExtension也相对独立,只有加载配置信息部分与getExtension共用了同一个 
方法。和获取普通扩展类一样,框架会先检查缓存中是否有已经初始化化好的Adaptive实例, 
没有则调用createAdaptiveExtension重新初始化。初始化过程分为4步:
和getExtension 一样先加载配置文件。

  1. 生成自适应类的代码字符串。
  2.  获取类加载器和编译器,并用编译器编译刚才生成的代码字符串。Dubbo 一共有三种 
  3. 类型的编译器实现。
  4. 返回对应的自适应类实例。

getExtension 的实现原理

ExtensionLoader#getExtension 会调用ExtensionLoader#createExtension 方法

    /***  创建扩展实例* @param name  别名* @return*/private T createExtension(String name) {Class<?> clazz = getExtensionClasses().get(name);if (clazz == null) {throw findException(name);}try {// 先尝试从缓存中获取实例T instance = (T) EXTENSION_INSTANCES.get(clazz);if (instance == null) {// 不存在的话则通过反射创建实例EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());instance = (T) EXTENSION_INSTANCES.get(clazz);}// 反射执行 setter 方法injectExtension(instance);Set<Class<?>> wrapperClasses = cachedWrapperClasses;if (wrapperClasses != null && !wrapperClasses.isEmpty()) {// 检查是否有包装类for (Class<?> wrapperClass : wrapperClasses) {// 通过反射创建包装类实例,再执行包装实例的 setter 方法, 最后更新包装类实例// 这里我们能看出 包装类需要 实现 接口,并且包装类需要有一个构造函数,构造参数是接口类型instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));}}return instance;} catch (Throwable t) {throw new IllegalStateException("Extension instance(name: " + name + ", class: " +type + ")  could not be instantiated: " + t.getMessage(), t);}}

getAdaptiveExtension 的实现原理

        在getAdaptiveExtension()方法中,会为扩展点接口自动生成实现类字符串,实现类主要包含以下逻辑:为接口中每个有^Adaptive注解的方法生成默实现(没有注解的方法则生成空实现),每个默认实现都会从URL中提取Adaptive参数值,并以此为依据动态加载扩展点。然后,框架会使用不同的编译器,把实现类字符串编译为自适应类并返回。

         生成完代码之后就要对代码进行编译,生成一个新的Classo Dubbo中的编译器也是一个自 
适应接口,但@Adaptive注解是加在实现类AdaptiveCompiler上的。这样一来AdaptiveCompiler 
就会作为该自适应类的默认实现,不需要再做代码生成和编译就可以使用了。

    private Class<?> createAdaptiveExtensionClass() {String code = createAdaptiveExtensionClassCode();ClassLoader classLoader = findClassLoader();com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();return compiler.compile(code, classLoader);}

// TODO :待续

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/501517.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何利用Logo设计免费生成器创建专业级Logo

在当今的商业世界中&#xff0c;一个好的Logo是品牌身份的象征&#xff0c;它承载着公司的形象与理念。设计一个专业级的Logo不再需要花费大量的金钱和时间&#xff0c;尤其是当我们拥有Logo设计免费生成器这样的工具时。接下来&#xff0c;让我们深入探讨如何利用这些工具来创…

游戏如何检测iOS越狱

不同于安卓的开源生态&#xff0c;iOS一直秉承着安全性更高的闭源生态&#xff0c;系统中的硬件、软件和服务会经过严格审核和测试&#xff0c;来保障安全性与稳定性。 据FairGurd观察&#xff0c;虽然iOS系统具备一定的安全性&#xff0c;但并非没有漏洞&#xff0c;如市面上…

智联视频超融合平台:电力行业的智能守护者

文章目录 一、远程实时监控与设备状态监测二、提高应急响应能力三、实现无人值守与减员增效四、保障电力设施安全与防范外部破坏五、提升电网运行管理效率与决策科学性六、助力电力企业数字化转型与智能化发展七、智联视频超融合平台 在当今数字化浪潮下&#xff0c;视频联网平…

卸载干净 IDEA(图文讲解)

目录 1、卸载 IDEA 程序 2、注册表清理 3、残留清理 1、卸载 IDEA 程序 点击屏幕左下角 Windows 图标 -> 设置-控制面板->intellij idea 勾选第一栏 Delete IntelliJ IDEA 2022.2 caches and local history&#xff0c;表示同时删除 IDEA 本地缓存以及历史。 Delete I…

计算机网络•自顶向下方法:路由选路算法

路由选路算法 在网络层中&#xff0c;选路是指数据包从源主机到目的主机的传输过程中&#xff0c;如何通过网络中的路由器选择一条合适的路径。路由器根据网络拓扑、路由表、协议规则等来决定如何将数据包转发到下一跳&#xff0c;直到数据包到达目的地。 选路算法分类 静态算…

Qemu配置QXL显卡支持分辨率

默认情况下&#xff0c;创建的vm的视频RAM限制为16MB。在win操作系统中分辨率最高就只能调到1024x768。 <video><model typecirrus vram16384 heads1 primaryyes/><address typepci domain0x0000 bus0x00 slot0x02 function0x0/> </video>单单修改ram…

【区块链】零知识证明基础概念详解

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 零知识证明基础概念详解引言1. 零知识证明的定义与特性1.1 基本定义1.2 三个核心…

Redis面试相关

Redis开篇 使用场景 缓存 缓存穿透 解决方法一&#xff1a; 方法二&#xff1a; 通过多次hash来获取对应的值。 小结 缓存击穿 缓存雪崩 打油诗 双写一致性 两种不同的要求 强一致 读锁代码 写锁代码 强一致&#xff0c;性能低。 延迟一致 方案一&#xff1a;消息队列 方…

以太网协议和LWIP协议详解

一、以太网协议简介 以太网是一种产生较早&#xff0c;使用相当广泛的局域网技术。目前以太网根据速度等级分类大概分为&#xff1a;标准以太网&#xff08;10Mbit/s&#xff09;&#xff0c;快速以太网&#xff08;100Mbit/s&#xff09;&#xff0c;千兆以太网&#xff08;1…

Qt|QWidget窗口支持旋转

功能实现&#xff1a;使用QWidget创建的窗口支持窗口旋转功能。 展示的示例中支持由水平方向旋转至垂直方向。至于其它角度旋转的问题&#xff0c;看完这篇文章后应该会很简单能实现的&#xff01; 开发环境&#xff1a;win VS2019 Qt 5.15.2 在实现之前也有想用使用 QProp…

微信小程序滑动解锁、滑动验证

微信小程序简单滑动解锁 效果 通过 movable-view &#xff08;可移动的视图容器&#xff0c;在页面中可以拖拽滑动&#xff09;实现的简单微信小程序滑动验证 movable-view 官方说明&#xff1a;https://developers.weixin.qq.com/miniprogram/dev/component/movable-view.ht…

微服务实战——购物车模块实战

购物车 1. 数据模型分析 1.1. 需求描述 用户可以在登录状态下将商品添加到购物车【用户购物车/在线购物车】 放入数据库mongodb放入 redis&#xff08;采用&#xff09; 登录以后&#xff0c;会将临时购物车的数据全部合并过来&#xff0c;并清空临时购物车&#xff1b; 用…

1961-2022年中国大陆多干旱指数数据集(SPI/SPEI/EDDI/PDSI/SC-PDSI/VPD)

DOI: 10.5194/essd-2024-270 干旱指数对于评估和管理缺水和农业风险至关重要;然而&#xff0c;现有数据集中缺乏统一的数据基础&#xff0c;导致不一致&#xff0c;对干旱指数的可比性提出了挑战。本研究致力于创建CHM_Drought&#xff0c;这是一个创新且全面的长期气象干旱数…

xilinx的高速接口构成原理和连接结构及ibert工具的使用-以k7 GTX为例

一、相关简介 Xilinx的高速接口称之为transceivers(高速收发器&#xff09;&#xff0c;这部分的电路是专用电路&#xff0c;供电等都是独立的&#xff0c;根据速率可以分为GTP/GTX/GTH/GTY/GTM等。 Xilinx的高速接口是QUAD为单位的&#xff0c;没一个QUAD由一个时钟COMMON资…

机器学习之模型评估——混淆矩阵,交叉验证与数据标准化

目录 混淆矩阵 交叉验证 数据标准化 0-1标准化 z 标准化 混淆矩阵 混淆矩阵&#xff08;Confusion Matrix&#xff09;是一种用于评估分类模型性能的工具。 它是一个二维表格&#xff0c;其中行表示实际的类别&#xff0c;列表示模型预测的类别。 假设我们有一个二分类问题&…

第R3周:RNN-心脏病预测

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 文章目录 一、前言二、代码流程1、导入包&#xff0c;设置GPU2、导入数据3、数据处理4、构建RNN模型5、编译模型6、模型训练7、模型评估 电脑环境&#xff1a;…

40% 降本:多点 DMALL x StarRocks 的湖仓升级实战

小编导读&#xff1a; 多点 DMALL 成立于2015年&#xff0c;持续深耕零售业&#xff0c;为企业提供一站式全渠道数字零售解决方案 DMALL OS。作为 DMALL OS 数字化能力的技术底座&#xff0c;大数据平台历经多次迭代平稳支撑了公司 To B 业务的快速开展。随着国家产业升级和云原…

C语言——字符函数和内存函数

目录 前言 字符函数 1strlen 模拟实现 2strcpy 模拟实现 3strcat 模拟实现 4strcmp 模拟实现 5strncpy 模拟实现 6strncat 模拟实现 7strncmp 模拟实现 8strstr 模拟实现 9strtok 10strerror 11大小写字符转换函数 内存函数 1memcpy 模拟实现 2…

职场常用Excel基础04-二维表转换

大家好&#xff0c;今天和大家一起分享一下excel的二维表转换相关内容~ 在Excel中&#xff0c;二维表&#xff08;也称为矩阵或表格&#xff09;是一种组织数据的方式&#xff0c;其中数据按照行和列的格式进行排列。然而&#xff0c;在实际的数据分析过程中&#xff0c;我们常…

软考教材重点内容 信息安全工程师 第 12 章网络安全审计技术原理与应用

12.1.1 网络安全审计概念 网络安全审计是指对网络信息系统的安全相关活动信息进行获取、记录、存储、分析和利用的工作。网络安全审计的作用在于建立“事后”安全保障措施&#xff0c;保存网络安全事件及行为信息&#xff0c;为网络安全事件分析提供线索及证据&#xff0c;以便…