spring注解方式整合Dubbo源码解析
系列文章目录
前言
本节我们的Dubbo源码版本基于2.6.x
在前一章我们的整合案例中,我们有几个比较关键的步骤:
- 在启动类上标注了
@EnableDubbo注解 - 在provider类上面标注了
@Service注解来提供dubbo服务 - 在消费的时候通过
@Reference注解引入dubbo服务 - 在配置文件中配置应用名,协议,暴露端口,注册中心地址等。
- 在配置文件中配置应用名,协议,暴露端口,注册中心地址等。
一、EnableDubbo注解
- 这里可以看到
EnableDubbo是一个复合注解,集成了@EnableDubboConfig和@DubboComponentScan注解
2.EnableDubbo注解的属性,其中scanBasePackages和scanBasePackageClasses默认都是空,这里是可以指定包扫描的路径或者基类的,然后multipleConfig默认是true。

1.1、EnableDubboConfig注解
- 这里使用
import注解导入了DubboConfigConfigurationRegistrar- 这里
EnableDubboConfig注解中的multiple属性默认为true

1.1.1、DubboConfigConfigurationRegistrar
- 首先获取
EnableDubboConfig注解中的属性列表- 这里会根据注解中的
multiple属性判断是否是单一绑定配置还是多个实例绑定配置,这里multiple默认为true。

这里单一实例和多实例的区别,如下所示,多实例就是协议我可能支持多种,这样会根据我们是否支持多实例来给每个协议都生成一个配置.

1.1.2、AnnotatedBeanDefinitionRegistryUtils#registerBeans
- 上面会根据单实例还是多实例的区别来判断传入的注解类是
DubboConfigConfiguration.Single还是DubboConfigConfiguration.Multiple.class,最终都会调用到registerBeans方法。- 这里会构造一个
AnnotatedBeanDefinitionReader类,然后会扫描上面传入的注解类,然后注入到spring容器中。

1.1.3、DubboConfigConfiguration
- 我们上面把
Single or Multiple注解到spring容器中,然后发现对应类上面也标注了@EnableDubboConfigBindings注解,二者的区别就是Multiple多了一个注解属性,multiple为true@EnableDubboConfigBindings注解中是一个@EnableDubboConfigBinding注解的数组,他的作用就是把配置文件中的配置信息和具体的类进行绑定。

1.1.4、EnableDubboConfigBindings注解
- 这里我们发现他也是通过import导入了一个
DubboConfigBindingsRegistrar类。

1.1.5、DubboConfigBindingsRegistrar#registerBeanDefinitions
- 上面通过
import导入了DubboConfigBindingsRegistrar,他是实现ImportBeanDefinitionRegistrar,spring就会在其生命周期中回调对应的registerBeanDefinitions`方法。- 这里首先会获取
EnableDubboConfigBindings注解中的属性列表- 然后获取
value属性,这里对应就是EnableDubboConfigBinding[]注解列表- 然后创建了一个
DubboConfigBindingRegistrar注册器- 把
spring的环境也就是ConfigurableEnvironment设置到注册器中,从environment中可以获取到配置文件中的配置信息- 遍历
EnableDubboConfigBinding[]注解列表,注册对应的BeanDefinition

1.1.6、DubboConfigBindingRegistrar#registerBeanDefinitions
例:@EnableDubboConfigBinding(prefix = “dubbo.applications”, type = ApplicationConfig.class, multiple = true),
- 这里首先会从
EnableDubboConfigBinding中解析出来prefix属性的value,得到dubbo.applications- 解析出来
type属性,得到ApplicationConfig.class- 解析出来
multiple属性,得到true- 把解析出来的参数全部传入调用
registerDubboConfigBeans注册Dubbo的配置Bean

1.1.7、DubboConfigBindingRegistrar#registerDubboConfigBeans
- 这里调用
getSubProperties方法从environment中,也就是我们配置文件中的配置里面解析出来指定前缀的配置- 如果没有指定前缀的配置则不需要注册
BeanDefinition- 根据
multiple属性,这里一般为true,这样我们如果配置了多种协议,那么就会生成多个beanName- 给每个
beanName都注册一个空的BeanDefinition,然后给每个Bean都注册一个DubboCOnfigBindingBeanPostProcessor的bean的后置处理器- 最后调用
registerDubboConfigBeanCustomizers注册了一个NamePropertyDefaultValueDubboConfigBeanCustomizer的bean

1.1.7.1、PropertySourcesUtils#getSubProperties
- 把
propertySources转成MutablePropertySources- 调用重载方法
getSubProperties

- 这里就是遍历
PropertySource,解析出来指定前缀的配置,然后缓存起来返回。

1.1.7.2、DubboConfigBindingRegistrar#resolveMultipleBeanNames
- 这里就是从配置中解析出来beanName。
比如配置是如下:
dubbo.protocols.p1.name=dubbo
dubbo.protocols.p1.port=20880
dubbo.protocols.p1.host=0.0.0.0
这里properties传进来的就是
p1.name=dubbo
p1.port=20880
…
那么解析出来的beanName就是p1

1.1.7.3、DubboConfigBindingRegistrar#registerDubboConfigBean
这里就是通过
BeanDefinitionBuilder生成一个指定类型的空的beanDefinition并注册到spring容器中。

1.1.7.4、DubboConfigBindingRegistrar#registerDubboConfigBindingBeanPostProcessor
- 这里为我们刚刚每个注册的配置bean都同样注册一个
DubboCOnfigBindingBeanPostProcessor类型的的bean后置处理器- 这里首先生成一个
DubboConfigBindingBeanPostProcessor类型的BeanDefinitionBuilder- 然后这里会设置构造器参数值,把前缀和beanName都传进去,这里的前缀如:
dubbo.registries.r2,beanName如:r2,这里设置构造器参数的value,那么spring帮我们创建bean的时候就会调用对应的构造器把参数值传进去- 通过
BeanDefinitionBuilder生成DubboConfigBindingBeanPostProcessor的BeanDefinition并注册到spring容器中。

1.1.7.5、DubboConfigBindingRegistrar#registerDubboConfigBeanCustomizers
这里会注册
namePropertyDefaultValueDubboConfigBeanCustomizer的bean到spring容器中,这里后续DubboConfigBindingBeanPostProcessor处理逻辑中会用到。

1.1.7.6、DubboConfigBindingBeanPostProcessor#postProcessBeforeInitialization
- 我们在上面会给每个配置bean都生成一个后置处理器,那么在spring容器的生命周期中就会调用到后置处理器的
postProcessBeforeInitialization方法,我们来看下DubboConfigBindingBeanPostProcessor的postProcessBeforeInitialization方法- 这里会判断beanName是否一致,只处理关联的那个bean
- 这里会调用bind方法从properties中获取值并设置到DubboConfig对象中
- 设置dubboConfig对象的name属性,设置为beanName

1.1.7.7、DubboConfigBindingBeanPostProcessor#bind
- 这里会调用dubboConfigBinder的bind方法,我们看下dubboConfigBinder是啥时候构造出来的,他的bind方法是如何实现数据绑定的。

我们看到在DubboConfigBindingBeanPostProcessor#afterPropertiesSet方法会进行初始化操作,这个方法也是spring提供的回调方法,会在属性设置前调用到这个方法

这里initDubboConfigBinder方法首先会从spring容器中获取DubboConfigBinder类型的bean,如果获取不到就创建默认的也就是DefaultDubboConfigBinder。


- 这里initConfigBeanCustomizers方法会从spring容器中获取DubboConfigBeanCustomizer类型的所有bean实现,这里最少会有一个,之前在DubboConfigBindingRegistrar中注册的namePropertyDefaultValueDubboConfigBeanCustomizer
- 然后设置到configBeanCustomizers属性中,并且会按照order进行排序.

1.1.7.8、DefaultDubboConfigBinder#bind
- 从上面我们知道如果我们没有注入DubboCOnfigBinder,则默认是DefaultDubboConfigBinder
- 这里会创建DataBinder,这个是spring提供的,用来实现数据绑定
- 这里设置忽略无用的属性和不能解析的属性
- 从properties中获取指定前缀的属性
- 把perperties转成MutablePropertyValues,调用dataBinder#bind方法执行绑定操作,这里会把MutablePropertyValues中的配置绑定到dubboConfig中。

1.1.7.9、DubboConfigBindingBeanPostProcessor#customize
- 这里会遍历从spring容器中获取的所有configBeanCustomizers,并调用其customize方法
- 这里如果我们没有配置,默认只有一个NamePropertyDefaultValueDubboConfigBeanCustomizer

这里其实就是判断如果DubboConfig中有name属性,则判断如果name属性如果没有设置值,则反射调用其set方法把beanName设置到name属性中。

1.2、DubboComponentScan注解
这里通过Import导入了DubboComponentScanRegistrar

1.2.1、DubboComponentScanRegistrar#registerBeanDefinitions
- 获取包扫描路径
- 注册处理@Service注解的后置处理器
- 注册处理@Reference注解的后置处理器

1.2.2、DubboComponentScanRegistrar#getPackagesToScan
- 获取DubboComponentScan注解中配置的属性
- 获取value属性,并把包路径数组转成set,可以对重复的包路径去重
- 如果指定了basePackageClasses,那么说明需要对这些类所在包路径设置到set中
- 如果都没指定包路径,则使用当前标注了@DubboComponentScan注解所在类的包路径作为基础包扫描路径。

1.2.3、DubboComponentScanRegistrar#registerServiceAnnotationBeanPostProcessor
- 注册ServiceAnnotationBeanPostProcessor后置处理器到spring容器中,并制定构造器的参数值为包扫描路径,这里spring会在生成bean的时候会调用构造方法设置进去

1.2.4、DubboComponentScanRegistrar#registerReferenceAnnotationBeanPostProcessor
- 注册registerReferenceAnnotationBeanPostProcessor后置处理器到spring容器中

1.2.5、ServiceAnnotationBeanPostProcessor#postProcessBeanDefinitionRegistry
- ServiceAnnotationBeanPostProcessor是一个BeanDefinitionRegistryPostProcessor,spring会回调他的postProcessBeanDefinitionRegistry方法来让用户修改BeanDefinition
- 这里会获取用户注解配置的包扫描
- 调用registerServiceBeans注册ServiceBean
1.2.5.1、ServiceAnnotationBeanPostProcessor#postProcessBeanDefinitionRegistry
这里就是做了下占位符的处理,因为有可能包路径中有占位符例:xxx.${xx}…xxx。

1.2.5.2、ServiceAnnotationBeanPostProcessor#postProcessBeanDefinitionRegistry
- 这里首先创建DubboClassPathBeanDefinitionScanner,用来执行包扫描
- 从registry中解析出BeanNameGenerator
- 设置的DubboClassPathBeanDefinitionScanner的beanNameGenerator为刚刚解析出来的BeanName生成器
- 给scanner设置IncludeFilter,表示只扫描标注了Dubbo中的@Service注解的类
- 遍历包路径,执行扫描操作,把扫描出来的BeanDifinition转换成BeanDefinitionHolder
- 遍历BeanDefinitionHolders,注册ServiceBean

1.2.5.3、ServiceAnnotationBeanPostProcessor#resolveBeanNameGenerator
- 判断如果BeanDefinitionRegistry是SingletonBeanRegistry类型,则获取internalConfigurationBeanNameGenerator返回
- 如果不是SingletonBeanRegistry或者从容器中没有获取到internalConfigurationBeanNameGenerator则使用AnnotationBeanNameGenerator

1.2.5.4、ServiceAnnotationBeanPostProcessor#findServiceBeanDefinitionHolders
- 获取出来packageToScan路径下标注了@Service的BeanDefinition
- 把扫描出来的BeanDefinition转换成BeanDefinitionHolder,Holder中包含BeanDefinition和BeanName

1.2.5.5、ServiceAnnotationBeanPostProcessor#registerServiceBean
- 从beanDefinitionHolde中解析出来对应的beanClass
- 从BeanClass上获取标注的@Service注解信息
- 从beanClass和@Service注解信息中解析出来对应实现的接口的class
- 从从beanDefinitionHolder中获取BeanName,构造ServiceBean的Definition,这个ServiceBean用来存储这个服务提供者的元数据信息
- 生成ServiceBean的BeanName
- 如果没有冲突,则会添加到spring容器中

1.2.5.5、ServiceAnnotationBeanPostProcessor#resolveClass
- 这里从BeanDefinition获取到对应的BeanClassName,然后通过BeanClassName解析出来对应的class对象

1.2.5.6、ServiceAnnotationBeanPostProcessor#resolveServiceInterfaceClass
- 首先拿@service注解中配置的interfaceClass属性,如果没有指定,在判断用户有没有在注解中指定interfaceName,如果指定了,则尝试解析出interfaceName对应的class返回
- 如果注解中没有配置,则获取到当前BeanClass实现的所有接口类,然后获取第一个

1.2.5.6、ServiceAnnotationBeanPostProcessor#buildServiceBeanDefinition
- 首先通过BeanDefinitionBuilder生成一个空的ServiceBean的BeanDefinition
- 这里获取BeanDefinition的propertyValues,然后设置忽略的属性名,因为这些都是对象类型,是我们之前解析配置文件生成的配置类,已经放到spring容器中,需要通过注入的方式特殊赋值
- 给BeanDefinition的propertyValues添加一个PropertyValues是AnnotationPropertyValuesAdapter,这个可以从environment获取对应的配置,后续spring在进行属性注入的时候会自动填充
- 下面就是调用addPropertyReference,这里会从spring容器中找到对应name的bean,然后把对应的bean设置到其属性中


1.2.5.7、ServiceAnnotationBeanPostProcessor#generateServiceBeanName
这里serviceBeanName的生成规则就是拼接了接口名+分组名+版本号生成一个beanName


1.2.5.8、ServiceBean#onApplicationEvent
- 在上面我们已经把ServiceBean注册到Spring容器中,那么他是什么时候进行服务暴露的,注册到注册中上,供外部服务调用,从下图我们可以看到他是继承了很多接口,其中一个就是ApplicationListener,并且监听的是容器刷新完成的时间。
- 那么Spring容器在刷新完成的时候就会回调监听器的onApplicationEvent方法,在这里会进行dubbo服务的导出操作,这个后续我们再进行分析。


1.2.6、ReferenceAnnotationBeanPostProcessor
- 这里他继承了AnnotationInjectedBeanPostProcessor,并且指定了要进行自动注入的注解是@Reference注解,这个AnnotationInjectedBeanPostProcessor的回调时机是在Bean属性填充完毕后,这里可以类比@Autowried注解,也是会有一个xxxProcessor来进行自动注入的逻辑

1.2.6.1 、AnnotationInjectedBeanPostProcessor#postProcessPropertyValues
- 这里首先会找到Bean所有标注了@Reference注解的字段和方法
- 然后对字段、方法进行反射绑定

1.2.6.2 、AnnotationInjectedBeanPostProcessor#findInjectionMetadata
- 这里首先从缓存中获取当前bean的注入元数据信息
- 如果缓存中没有,则会调用buildAnnotatedMetadata方法构造注解元数据

1.2.6.3 、AnnotationInjectedBeanPostProcessor#buildAnnotatedMetadata
- 这里首先查找这个类标注了@Reference注解的属性元素
- 再查找这个类标注了@Reference注解的方法元素
- 组装成AnnotatedInjectionMetadata返回

1.2.6.4 、AnnotationInjectedBeanPostProcessor#buildAnnotatedMetadata
- 这里就是遍历这个类对应的所有属性,判断上面有没有标注@Reference注解
- 判断如果有标注注解,如果修饰符是static,那么也不支持
- 如果满足条件则加入到元素列表中返回

1.2.6.5 、AnnotationInjectedBeanPostProcessor#findAnnotatedMethodMetadata
- 这里遍历类中的所有方法,这里有可能方法是个泛型方法,因为泛型在编译阶段会进行类型擦除,Java编译器会将类型参数替换为其上界(类型参数中extends子句的类型),如果上界没有定义,则默认为Object,当一个子类在继承(或实现)一个父类(或接口)的泛型方法时,在子类中明确指定了泛型类型,那么在编译时编译器会自动生成桥接方法,这样是为了避免子父类方法签名不一致,就在子类自动生成一个与父类的方法签名一致的桥接方法
- 所以这里会判断如果是桥接方法就找到被桥接的方法,然后判断被桥接的方法是否可见,方法上面有没有标注@Reference注解
- 如果有注解,则判断是不是静态,不支持静态方法,也不支持无参方法,因为这样没法进行注入了,不知道注入啥类型的
- 把符合条件的方法收集返回

1.2.6.5 、InjectionMetadata#inject
- 在上面已经查询到了并组装成InjectionMetadata,这里会调用inject方法执行注入操作
- 这里是spring的源码,会遍历每个element,并调用其inject方法

1.2.6.6 、InjectionMetadata#inject
在上面调用每个element的inject方法就会调用到element具体的实现,是方法的还是属性的

1.2.6.6.1 、AnnotatedFieldElement#inject
- 获取属性类型
- 获取注入的对象
- 通过反射设置到属性中

1.2.6.6.2、AnnotatedFieldElement#inject
- 获取属性类型
- 获取注入的对象
- 通过反射设置

1.2.6.7 、AnnotationInjectedBeanPostProcessor#getInjectedObject
- 首先构造缓存key,从缓存中获取
- 如果缓存中不存在,则调用doGetInjectedBean获取注入的对象

1.2.6.8 、ReferenceAnnotationBeanPostProcessor#doGetInjectedBean
- 首先构造serviceBean的名字,通过接口+分组+版本构造
- 调用buildReferenceBeanIfAbsent方法,生成ReferenceBean对象
- 放入缓存中
- 生成代理对象

1.2.6.9 、ReferenceAnnotationBeanPostProcessor#buildReferenceBeanIfAbsent
- 先从缓存中获取referenceBean
- 如果缓存中没有,则通过ReferenceBeanBuilder生成一个referenceBean

1.2.6.9.1 、ReferenceBeanBuilder#build
- 这里checkDependencies是个空方法
- 调用doBuild创建一个ReferenceBean
- 调用configureBean配置ReferenceBean的相关属性

1.2.6.9.2 、ReferenceBeanBuilder#doBuild
- 这里直接创建一个ReferenceBean返回

1.2.6.9.3、AbstractAnnotationConfigBeanBuilder#doBuild
- ConfigureBean的前置操作
- 然后设置注册中心配置,监控中心配置,应用配置,模块配置等
- ConfigureBean的后置操作

1.2.6.9.4、ReferenceBeanBuilder#preConfigureBean
- 这里又使用到了DataBinder来给referenceBean来做数据绑定
- 这里给filter和listener属性注册了StringTrimmerEditor,他会帮助格式化String,去除前后空格
- 处理parameters属性,通过自定义的PropertyEditorSupport,把String处理成map
- 调用dataBinder#bind方法,这里会自动给referenceBean绑定配置文件中的相关配置

1.2.6.9.5、AbstractAnnotationConfigBeanBuilder#configureRegistryConfigs
这里设置配置的逻辑一致,都是从@Reference注解中获取对应的配置名,然后从spring容器中找到对应的配置Bean,然后设置到属性中。

1.2.6.9.6、ReferenceBeanBuilder#postConfigureBean
- 这里还是设置一些属性到ReferenceBean中,其中具体就不细看了,和上面类似

1.2.6.10 、ReferenceAnnotationBeanPostProcessor#cacheInjectedReferenceBean
这里就是把对应的注入点和对应的注入值放入缓存中

1.2.6.11 、ReferenceAnnotationBeanPostProcessor#buildProxy
- 调用buildInvocationHandler方法构造一个InvocationHandler
- 创建一个proxy对象返回,这里需要代理对象是因为Dubbo在调用的时候就算是本地服务也有负载均衡,注册中心等一系列其他的逻辑,因此不能直接返回服务实现类,而是返回代理对象,公共的操作让代理对象去执行

1.2.6.12 、ReferenceAnnotationBeanPostProcessor#buildInvocationHandler
.1 先从缓存中获取,如果缓存中没有就创建一个
2. 判断当前spring容器中有没有包含着这个引用的bean,如果有说明是本地调用,则缓存起来
3. 如果是远程调用,则会调用ReferenceBeanInvocationHandler#init方法

1.2.6.12 、ReferenceBeanInvocationHandler#init
- 这里会调用referenceBean#get方法,会返回一个代理对象
- 这里#referenceBean#get方法是服务引入的核心逻辑,我们后续章节剖析

最后这里返回的代理对象会被设置到对应标注了@Reference的属性中,然后调用其方法都会走到代理对象这里,Dubbo会在这里面做一系列远程调用,服务容错,服务逻辑的逻辑。
相关文章:
spring注解方式整合Dubbo源码解析
系列文章目录 前言 本节我们的Dubbo源码版本基于2.6.x 在前一章我们的整合案例中,我们有几个比较关键的步骤: 在启动类上标注了EnableDubbo注解在provider类上面标注了Service注解来提供dubbo服务在消费的时候通过Reference注解引入dubbo服务在配置文件…...
大数值金额大写转换(C语言)
关于大数值金额大写转换,在财务管理的应用方面没什么意义。一般来说,千亿级,万亿级的数值就够了。因为在国家级层面是以亿为单位的,也就表达为千万亿,万万亿。在企业层面数值金额转换设置到千亿、万亿就行了。大的集团…...
迷宫问题图解 : 基于骨架提取、四邻域
目录 1. 迷宫的连通域 2. How to remove branch ? 3. 基于4邻域的 remove 分支 3.1 找到分支的端点 3.2 4邻域的 remove 分支 3.3 循环移除分支 3.4 code 4. 迷宫路线 4.1 预处理 4.2 提取骨架 4.3 分支的端点 4.4 去除分支的端点 4.5 循环去除分支 4…...
设计模式 - 如何在库和主程序之间互相调用数据和函数
背景:在项目开发过程中,难免碰到这种情况,当我们想要通过我们开发的库,调用主程序中的一些变量或者函数的时候,就会导致一些问题,因为在项目构建过程中,库都是不依赖于主程序编译的,…...
Redis面试题:1~2亿条数据需要缓存,请问如何设计这个存储案例
目录 前言 一、哈希取余分区 优点 缺点 二、一致性哈希算法分区 背景 步骤 ① 算法构建一致性哈希环 ② 服务器IP节点映射 ③ key落到服务器的落键规则 优点 ① 容错性 ② 扩展性 缺点 三、哈希槽分区 前言 单机单台100%不可能,肯定是分布式存储&am…...
程序员必备的软技能-《如何阅读一本书》
阅读很重要,我们真的会阅读吗? 这本书的初版是 1940年,时隔 80年,其内容仍然不过时。第一次读这本书时,给我最大的影响就是主题阅读,每次学习一个新理论、技术,都入手多本关于这项理论、技术的书…...
Java数据结构-栈、队列常用类(Stack、ArrayDeque、LinkedLList)
数据结构的三要素包括:逻辑结构、存储结构、数据的运算。逻辑结构描述的是数据之间的逻辑关系,分为线性结构(线性表(数组、链表)、栈、队列)和非线性结构(图、树、集合)。物理结构也…...
拯救了大批爬虫程序员,因为一个简单的神器
相信大家应该都写过爬虫,简单的爬虫只需要使用 requests 即可。遇到复杂的爬虫,就需要在程序里面加上请求头和参数信息。类似这种:我们一般的步骤是,先到浏览器的网络请求中找到我们需要的请求,然后将请求头和参数信息…...
2023年美赛C题Wordle预测问题三、四建模及Python代码详细讲解
更新时间:2023-2-19 16:30 相关链接 (1)2023年美赛C题Wordle预测问题一建模及Python代码详细讲解 (2)2023年美赛C题Wordle预测问题二建模及Python代码详细讲解 (3)2023年美赛C题Wordle预测问题三、四建模…...
相关性-回忆录(持续更新)
1.TODO方向 (1)数据增强:finetuning阶段需要大量人工标注样本,消耗时间和成本。用户点击数据作为弱监督学习,可以尝试图网络构建节点和边(query聚合); 使用展现未点击生成对抗网络进…...
(必备技能)使用Python实现屏幕截图
(必备技能)使用Python实现屏幕截图 文章目录 (必备技能)使用Python实现屏幕截图 一、序言二、环境配置 1、下载pyautogui包2、下载opencv-python包3、下载PyQt5包4、下载pypiwin32包 三、屏幕截屏源码与解析 1、使用pyautogui方法实现截屏2、使用PyQt方法实现截屏 a.获取窗口…...
「数据仓库」怎么选择现代数据仓库?
构建自己的数据仓库时要考虑的基本因素我们用过很多数据仓库。当我们的客户问我们,对于他们成长中的公司来说,最好的数据仓库是什么时,我们会根据他们的具体需求来考虑答案。通常,他们需要几乎实时的数据,价格低廉&…...
6.3 使用 Swagger 生成 Web API 文档
第6章 构建 RESTful 服务 6.1 RESTful 简介 6.2 构建 RESTful 应用接口 6.3 使用 Swagger 生成 Web API 文档 6.4 实战:实现 Web API 版本控制 6.3 使用 Swagger 生成 Web API 文档 高质量的 API 文档在系统开发的过程中非常重要。本节介绍什么是 Swaggerÿ…...
Day894.加锁规则的一些问题 -MySQL实战
加锁规则的一些问题 Hi,我是阿昌,今天学习记录的是关于加锁规则的一些问题的内容。 加锁规则,这个规则中,包含了两个“原则”、两个“优化”和一个“bug”: 原则 1:加锁的基本单位是 next-key lock。nex…...
【Flutter入门到进阶】Dart进阶篇---Dart异步编程
1 并行与并发的编程区别 1.1 并发与并行 1.1.1 说明 我们举个例子,如果有条高速公路 A 上面并排有 8 条车道,那么最大的并行车辆就是 8 辆此条高速公路 A 同时并排行走的车辆小于等于 8 辆的时候,车辆就可以并行运行。 CPU 也是这个原理,一个 CPU 相当于一个高速公路 A,核心数…...
点云配准方法原理(NDT、ICP)
配准是点云处理中的一个基础问题,众多学者此问题进行了广泛而深入的研究,也出现了一系列优秀成熟的算法,在三维建模、自动驾驶等领域发挥着重要的作用。 本文主要介绍粗配准NDT (Normal Distribution Transform) 与 精配准ICP (Iterative Cl…...
大规模 IoT 边缘容器集群管理的几种架构-0-边缘容器及架构简介
📚️Reference: IoT 边缘计算系列文章 什么是边缘容器? 边缘容器的概念 边缘容器是分散的计算资源,尽可能靠近最终用户或设备,以减少延迟、节省带宽并增强整体数字体验。 可以访问互联网的设备数量每天都在增加。有包括但不限于…...
代码随想录算法训练营第45天动态规划 背包基础 1 2、 416. 分割等和子集
文章目录01背包基础 (二维数组)思路递推公式初始化遍历顺序一维dp数组(滚动数组)一维数组的递推公式遍历顺序LeetCode 416. 分割等和子集思路总结01背包基础 (二维数组) 思路 根据动态规划五部进行分析&a…...
QT学习记录(六)类对象属性
类对象属性用来描述类对象的一些信息和当前的状态。类对象属性可以由类的编写者在编写类的时候定义,也可以由类的使用者在使用对象的时候定义。 由类的编写者定义 QPROPERTY()宏就是用来定义一个对象属性。 以第二行属性举例 QPROPERTY(bool enabled READ isEnabl…...
Spring Cloud Alibaba从搭建到源码完整进阶教程
微服务简介 Spring Cloud Alibaba 微服务简介 Nacos注册中心配置中心 Spring Cloud Nacos实战(一)- 下载和安装 Spring Cloud Nacos实战(二)- 服务提供者注册 Spring Cloud Nacos实战(三)- 服务消费者…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...

