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实战(三)- 服务消费者…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
基于Java+VUE+MariaDB实现(Web)仿小米商城
仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...
CTF show 数学不及格
拿到题目先查一下壳,看一下信息 发现是一个ELF文件,64位的 用IDA Pro 64 打开这个文件 然后点击F5进行伪代码转换 可以看到有五个if判断,第一个argc ! 5这个判断并没有起太大作用,主要是下面四个if判断 根据题目…...
拟合问题处理
在机器学习中,核心任务通常围绕模型训练和性能提升展开,但你提到的 “优化训练数据解决过拟合” 和 “提升泛化性能解决欠拟合” 需要结合更准确的概念进行梳理。以下是对机器学习核心任务的系统复习和修正: 一、机器学习的核心任务框架 机…...
华为OD机考- 简单的自动曝光/平均像素
import java.util.Arrays; import java.util.Scanner;public class DemoTest4 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint[] arr Array…...

