当前位置: 首页 > news >正文

Dubbo 3.x源码(22)—Dubbo服务引用源码(5)服务引用bean的获取以及懒加载原理

基于Dubbo 3.1,详细介绍了Dubbo服务的发布与引用的源码。

此前我们学习了Dubbo3.1版本的服务引入的总体流程,当然真正的服务远程引入、以及配置迁移啥的都还没讲,但是本次我们先不接着讲MigrationRuleListener#onRefer方法,而是先学习服务引用bean的获取以及懒加载原理。

Dubbo 3.x服务引用源码:

  1. Dubbo 3.x源码(11)—Dubbo服务的发布与引用的入口
  2. Dubbo 3.x源码(18)—Dubbo服务引用源码(1)
  3. Dubbo 3.x源码(19)—Dubbo服务引用源码(2)
  4. Dubbo 3.x源码(20)—Dubbo服务引用源码(3)
  5. Dubbo 3.x源码(21)—Dubbo服务引用源码(4)
  6. Dubbo 3.x源码(22)—Dubbo服务引用源码(5)服务引用bean的获取以及懒加载原理
  7. Dubbo 3.x源码(23)—Dubbo服务引用源码(6)MigrationRuleListener迁移规则监听器

Dubbo 3.x服务发布源码:

  1. Dubbo 3.x源码(11)—Dubbo服务的发布与引用的入口
  2. Dubbo 3.x源码(12)—Dubbo服务发布导出源码(1)
  3. Dubbo 3.x源码(13)—Dubbo服务发布导出源码(2)
  4. Dubbo 3.x源码(14)—Dubbo服务发布导出源码(3)
  5. Dubbo 3.x源码(15)—Dubbo服务发布导出源码(4)
  6. Dubbo 3.x源码(16)—Dubbo服务发布导出源码(5)
  7. Dubbo 3.x源码(17)—Dubbo服务发布导出源码(6)

文章目录

  • 1 服务引用bean的获取以及懒加载原理
  • 2 createLazyProxy创建懒加载代理对象
  • 3 DubboReferenceLazyInitTargetSource目标源
  • 4 代理对象层次以及懒加载的原理
  • 5 总结

1 服务引用bean的获取以及懒加载原理

上面的几篇文章中,我们学习了Dubbo 服务引入的流程,我们知道,在进行了服务引入并创建了服务引入Invoker之后,在最后会调用proxyFactory.getProxy方法根据invoker创建一个服务接口代理对象返回,并且赋值给ReferenceConfig的ref属性。

而我们知道,在业务代码中,通过@DubboReference注解实际引入的也是一个服务接口代理对象实例。

那么,这两个代理对象就是同一个对象吗?还是说,业务代码中实际引用的是另一个代理对象呢?
首先,我们此前就学习过,reference xml标签,或者@DubboReference注解等方式引入的服务,最终会被构建为一个ReferenceBean实例并存入spring容器。

ReferenceBean实现了FactoryBean接口,那么,我们实际上在业务代码中注入的服务接口代理对象实例来自于它的getObject方法的实现。

/*** Create bean instance.** <p></p>* Why we need a lazy proxy?** <p/>* When Spring searches beans by type, if Spring cannot determine the type of a factory bean, it may try to initialize it.* The ReferenceBean is also a FactoryBean.* <br/>* (This has already been resolved by decorating the BeanDefinition: {@link DubboBeanDefinitionParser#configReferenceBean})** <p/>* In addition, if some ReferenceBeans are dependent on beans that are initialized very early,* and dubbo config beans are not ready yet, there will be many unexpected problems if initializing the dubbo reference immediately.** <p/>* When it is initialized, only a lazy proxy object will be created,* and dubbo reference-related resources will not be initialized.* <br/>* In this way, the influence of Spring is eliminated, and the dubbo configuration initialization is controllable.*** @see DubboConfigBeanInitializer* @see ReferenceBeanManager#initReferenceBean(ReferenceBean)* @see DubboBeanDefinitionParser#configReferenceBean*** ReferenceBean的方法* * 创建bean实例。**/
@Override
public T getObject() {//如果懒加载的代理对象为null,那么创建要给懒加载的代理对象if (lazyProxy == null) {createLazyProxy();}//返回懒加载的代理对象return (T) lazyProxy;
}

可以看到该方法有很长的注释说明,并且该方法会返回一个懒加载的代理对象。但是,实际上目前版本Dubbo服务默认情况下都是随着应用的启动而引入的,真正调用懒加载对象的方法的时候,对应的Dubbo服务已经引入了。

所以这里的懒加载的目的并不是为了节省启动时间那么简单,那么为什么我们需要一个懒惰的代理?基于官方的注释如下:

  1. 当Spring按类型搜索bean时,如果Spring不能确定工厂bean的类型,它可能会尝试初始化它。ReferenceBean也是一个工厂bean。(这已经通过DubboBeanDefinitionParser.configReferenceBean方法解决了,解决方法就是为ReferenceBean设置decoratedDefinition并且设置beanClass为接口的class。)
  2. 此外,如果一些referencebean依赖于很早就初始化的bean,而dubbo配置bean还没有准备好,那么如果立即初始化dubbo引用,将会出现许多意想不到的问题。
  3. 当它初始化时,只会创建一个惰性代理对象,并且不会初始化与dubbo引用相关的资源。这样就消除了Spring的影响,并且dubbo配置初始化是可控的。

2 createLazyProxy创建懒加载代理对象

为当前服务接口创建一个懒加载代理对象,到这里我们明白了,业务代码中实际引用的服务接口代理对象实例和ReferenceConfig内部的服务接口代理对象实例ref并不是同一个代理对象。

这里代理对象是通过ProxyFactory代理工厂创建的对象,而ProxyFactory是spring包中的类,spring aop也是是通过ProxyFactory创建代理对象的,我们在此前学习spring aop源码的时候就学过它,具体的AOP创建代理对象的源码可以看之前的文章:https://blog.csdn.net/weixin_43767015/article/details/109851001

最终,我们创建的代理及接口实例是基于JDK的动态代理,并且实现了目标代理接口、EchoService、Destroyable这三个接口,targetSource目标源为DubboReferenceLazyInitTargetSource。

/*** ReferenceBean的方法* <p>* 创建引用服务接口的懒加载的代理对象*/
private void createLazyProxy() {//set proxy interfaces//see also: org.apache.dubbo.rpc.proxy.AbstractProxyFactory.getProxy(org.apache.dubbo.rpc.Invoker<T>, boolean)//新建一个ProxyFactory代理工厂对象,用于创建代理//这里的ProxyFactory是spring包中的类ProxyFactory proxyFactory = new ProxyFactory();//创建DubboReferenceLazyInitTargetSource对象添加到proxyFactory的targetSource属性中,通过此可以获取源目标对象proxyFactory.setTargetSource(new DubboReferenceLazyInitTargetSource());//代理对象需要实现的接口:引用服务接口proxyFactory.addInterface(interfaceClass);//代理对象需要实现的内部接口:EchoService、DestroyableClass<?>[] internalInterfaces = AbstractProxyFactory.getInternalInterfaces();for (Class<?> anInterface : internalInterfaces) {proxyFactory.addInterface(anInterface);}if (!StringUtils.isEquals(interfaceClass.getName(), interfaceName)) {//add service interfacetry {Class<?> serviceInterface = ClassUtils.forName(interfaceName, beanClassLoader);proxyFactory.addInterface(serviceInterface);} catch (ClassNotFoundException e) {// generic call maybe without service interface class locally}}/** 通过proxyFactory获取代理对象*/this.lazyProxy = proxyFactory.getProxy(this.beanClassLoader);
}

3 DubboReferenceLazyInitTargetSource目标源

每个代理对象保存了一个targetSource对象,这个targetSource对象内部封装了一个AOP的目标对象也就是被代理对象,通过getTarget方法可获取目标对象,然后就能通过目标对象调用被代理的原始方法了。

dubbo服务代理对象的目标源是一个DubboReferenceLazyInitTargetSource对象,它是ReferenceBean类的一个内部类,我们来看看它的实现。

/*** ReferenceBean的方法** @return 获取调用的代理对象*/
private Object getCallProxy() throws Exception {//如果ReferenceBean内部的referenceConfig不存在则抛出异常if (referenceConfig == null) {throw new IllegalStateException("ReferenceBean is not ready yet, please make sure to call reference interface method after dubbo is started.");}//get reference proxy/** 返回referenceConfig内部的代理引用服务实例ref*/return referenceConfig.get();
}private class DubboReferenceLazyInitTargetSource extends AbstractLazyCreationTargetSource {/*** 获取代理目标对象*/@Overrideprotected Object createObject() throws Exception {return getCallProxy();}/*** 获取代理目标接口*/@Overridepublic synchronized Class<?> getTargetClass() {return getInterfaceClass();}
}

4 代理对象层次以及懒加载的原理

getCallProxy方法内部调用的ReferenceConfig#get方法我们在本文的最开始就学习过了,将会返回内部的ref,那么一切都变得明朗起来,实际上,业务代码中获取的代理对象内部的代理目标对象,就是ReferenceConfig内部的服务接口代理对象实例ref,这就是它们之间的关系。

如果我们对应某个引用的服务设置属性init = false,那么在此前讲的DefaultModuleDeployer#referServices方法批量引用服务的时候,在shouldInit方法就会返回false,那么就不会调用ReferenceConfig#get方法,自然在启动的时候就不会去真正的进行服务引用。

而当我们调用代理对象的方法的时候,在获取代理目标对象的时候,getCallProxy方法中会调用ReferenceConfig#get方法,这样就把对于服务的引用从服务启动的时候延迟到了真正调用服务接口的时候,这就是Dubbo懒加载的实现原理。

真正调用的时候,调用逻辑为:业务引入的接口代理对象(ReferenceBean内部的lazyProxy)-> 代理目标对象(ReferenceConfig内部的接口代理对象ref),后续就是InvokerInvocationHandler、Invoker等真正dubbo相关的调用处理逻辑了,这些我们在后面dubbo服务调用的文章中讲解。

5 总结

本次我们学习了Dubbo服务引用bean的获取以及懒加载原理。

接下来我们将会继续学习MigrationRuleListener#onRefer方法,该方法才是真正的服务引入入口,MigrationRuleListener以及真正的服务引入的逻辑,以及服务迁移到底是个什么东西?我们后面学习。

相关文章:

Dubbo 3.x源码(22)—Dubbo服务引用源码(5)服务引用bean的获取以及懒加载原理

基于Dubbo 3.1&#xff0c;详细介绍了Dubbo服务的发布与引用的源码。 此前我们学习了Dubbo3.1版本的服务引入的总体流程&#xff0c;当然真正的服务远程引入、以及配置迁移啥的都还没讲&#xff0c;但是本次我们先不接着讲MigrationRuleListener#onRefer方法&#xff0c;而是先…...

nodejs——原型链污染

一、引用类型皆为对象 原型和原型链都是来源于对象而服务于对象的概念&#xff0c;所以我们要先明确一点&#xff1a; JavaScript中一切引用类型都是对象&#xff0c;对象就是属性的集合。 Array类型、Function类型、Object类型、Date类型、RegExp类型等都是引用类型。 也就…...

忘记 iPhone 密码:如果忘记密码,如何解锁 iPhone

为了提高个人数据的安全性&#xff0c;用户通常会为不同的帐户和设备创建不同的复杂密码。虽然较新的 iPhone 型号具有生物识别和面部解锁功能&#xff0c;但这些功能并不总是有效 - 如果您忘记了 iPhone 的密码&#xff0c;您可能会遇到麻烦。 iPhone 用户和 Android 用户一样…...

案例 采用Springboot默认的缓存方案Simple在三层架构中完成一个手机验证码生成校验的程序

案例 Cacheable 是 Spring Framework 提供的一个注解&#xff0c;用于在方法执行前先检查缓存&#xff0c;如果缓存中已存在对应的值&#xff0c;则直接返回缓存中的值&#xff0c;而不执行该方法体。如果缓存中不存在对应的值&#xff0c;则执行方法体&#xff0c;并将方法的…...

第四届人工智能、机器人和通信国际会议(ICAIRC 2024)

第四届人工智能、机器人和通信国际会议&#xff08;ICAIRC 2024&#xff09; 2024 4th International Conference on Artificial Intelligence, Robotics, and Communication 2024年12月27-29日 | 中国厦门 重要信息 会议官网&#xff1a;www.icairc.net 录用通知时间&…...

ctr/cvr预估之FM模型

ctr/cvr预估之FM模型 在数字化时代&#xff0c;广告和推荐系统的质量直接影响着企业的营销成效和用户体验。点击率&#xff08;CTR&#xff09;和转化率&#xff08;CVR&#xff09;预估作为这些系统的核心组件&#xff0c;其准确性至关重要。传统的机器学习方法&#xff0c;如…...

HAL-DMA中断空闲接受不定长数据

title: HAL-DMA中断空闲接受不定长数据 tags: STM32HalCubemax 面对无规律长度的数据帧如何处理&#xff1f; 不定长数据接收可以使用每帧数据发送完成后会有一定的空闲时间"帧的时间间隔?" 如果你想每帧都要可以采用dma加空闲中断的方式空闲中断一次就是一帧数据…...

【会议征稿,CPS出版】第四届管理科学和软件工程国际学术会议(ICMSSE 2024,7月19-21)

第四届管理科学和软件工程国际学术会议(ICMSSE 2024)由ACM珠海分会&#xff0c;广州番禺职业技术学院主办&#xff1b;全国区块链行业产教融合共同体&#xff0c;AEIC学术交流中心承办&#xff0c;将于2024年7月19-21日于广州召开。 会议旨在为从事管理与软件工程领域的专家学…...

无引擎游戏开发(3):数据结构设计|功能函数完善

为了简单起见&#xff0c;我们将棋盘的二维数组定义为全局变量。除此之外还要定义一个char类型的全局变量来识别当前的落子类型&#xff0c;我们将其初始化为‘O’。 char Board_data[3][3] {{-, -, -},{-, -, -},{-, -, -}, };char Cur_piece O; 现在回到“读取操作”部分…...

Laravel 高级:了解$loop

Blade 提供 foreach、while、for 和 forelse 等指令来与 PHP 循环配合使用。 您知道吗... 这些指令中有一个方便的 $loop 变量&#xff0c;它指示当前循环迭代&#xff1f;在本文中&#xff0c;我们将探索 $loop 和 loop 指令。&#x1f60e; 使用$loop比foreach更深入 该for…...

深入理解指针(1)

目录&#xff1a; 1. 内存和地址 2. 指针变量和地址 3. 指针变量类型的意义 4. const修饰指针 5. 指针运算 6. 野指针 7. assert断⾔ 8. 指针的使⽤和传址调用 1. 内存和地址 1.1 内存 在讲内存和地址之前&#xff0c;我们想有个⽣活中的案例&#xff1a; 假设有⼀栋宿舍楼&a…...

在无线网中 2.4G、5G、WiFi6、WiFi7 都是什么意思?

有同学问我在无线网中 2.4G/5G/WiFi6/WiFi7 都是什么意思&#xff1f;其实这是两个概念&#xff0c; 2.4G/5G 是频段&#xff0c;WiFi6/WiFi7 是无线协议的版本&#xff0c;千万别把版本和频段搞混了。 WiFi 协议是一系列基于 IEEE 802.11 标准的无线局域网技术协议&#xff0…...

milvus元数据解析工具milvusmetagui介绍使用

简介 milvusmetagui是一款用来对milvus的元数据进行解析的工具&#xff0c;milvus的元数据存储在etcd上&#xff0c;而且经过了序列化&#xff0c;通过etcd-manager这样的工具来查看是一堆二进制乱码&#xff0c;因此开发了这个工具对value进行反序列化解析。 在这里为了方便交…...

LabVIEW电磁超声热态金属在线缺陷检测系统

LabVIEW软件开发的电磁超声热态金属在线缺陷检测系统针对极端高温环境下的金属材料&#xff0c;进行实时、无损的缺陷检测&#xff0c;具有高精度和高可靠性&#xff0c;能够显著提高材料质量控制的效率和准确性。 项目背景 随着工业技术的发展&#xff0c;高温环境下的金属材…...

leecode代码模板

二分算法&#xff1a; 34. 在排序数组中查找元素的第一个和最后一个位置给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target&#xff0c;返回 [-1, -1]。你必须设计…...

可靠性测试及模型计算

双85高温高湿测试 场景描述&#xff1a; 85℃温度 85%湿度 老化测试 目的&#xff1a; 衡量产品使用寿命 反向推导&#xff1a; 如何根据产品寿命及工况计算双85测试时间 模型介绍 本质是化学反应速率&#xff08;老化的本质是&#xff09;随温度的变化 温湿度循环测…...

【Tools】 深入了解Burp Suite:Web应用抓包利器

唱 情 歌 齐齐来一遍 无时无刻都记住掌声 响遍天 来唱 情 歌 由从头再一遍 如情浓有点泪流难避免 音阶起跌拍子改变 每首歌 是每张脸 喜欢我 别遮脸 任由途人发现 &#x1f3b5; 刘德华《十七岁》 在Web应用和移动应用的开发与测试过程中&#xff0c;抓包…...

技术先进、应用广泛、社区活跃的[项目名称]

项目介绍 ----  [项目介绍内容]&#xff0c;此项目在开源社区中备受欢迎&#xff0c;其创新性技术和广泛应用领域吸引了大量开发者关注。  代码解释 ----  [代码解释内容]&#xff0c;该项目采用[编程语言]&#xff0c;通过[技术栈]实现&#xff0c;具有[功能特点]。  …...

Vue中data的属性可以和methods中方法同名吗,为什么?

在Vue中&#xff0c;data的属性不可以和methods中的方法同名&#xff0c;原因如下&#xff1a; 命名规范&#xff1a;从编程规范的角度来看&#xff0c;同名属性或方法可能会导致混淆和难以维护的代码。data通常用于存储组件的状态或数据&#xff0c;而methods则包含组件的行为…...

Esxi上创建windows 11虚拟机

下载windows 11系统镜像 Download Windows 11 (microsoft.com) 虚拟机配置 正常安装部署&#xff0c;需要注意以下几点&#xff1a; 1.cpu开启虚拟化&#xff0c;启用CPU热添加 2.内存开启热插拔 3.磁盘类型最好选择精简置备&#xff08;磁盘只使用最初所需要的数据存储空间…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

(一)单例模式

一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...