SpringMVC源码分析(二)启动过程之RequestMappingHandlerMapping分析
a、http请求中的url是如何与对应Handler的即Controller对应method映射的?
在上篇中提到在SpringMVC中,初始化9大内置组件的时候其中有一个组件就是HandlerMapping,在初始化HandlerMapping的时候会加载代码中所有标注了@Controller和@RequestMapping的类到spring容器中,作为一个个bean对象。
关键类RequestMappingHandlerMapping
类图上看出RequestMappingInfoHandlerMapping继承了AbstractHandlerMethodMapping。实现了InitializingBean接口并且实现了afterPropertiesSet方法。所以在spring初始化这个RequestMappingHandlerMapping对象的时候会进入到afterPropertiesSet()中,这个里面会调用父类AbstractHandlerMethodMapping的afterPropertiesSet(),然后调用initHandlerMethods()。在其中会初始化所有的HandlerMethods。

1、initHandlerMethods()
在当前方法中,主要做了几件事:
- 扫描所有的Handler类,获取所有带有@Controller或@RequestMapping注解的类。
- 遍历每个Handler类,获取类中的所有方法。
- 对于每个方法,判断是否存在@RequestMapping注解。
- 如果存在@RequestMapping注解,则解析该注解,获取其中的属性值,如请求路径、请求方法、请求参数等。
- 根据解析到的属性值,生成一个RequestMappingInfo对象,该对象代表了一个请求路径和请求方法的映射关系。
- 将生成的RequestMappingInfo对象与对应的HandlerMethod对象进行关联,形成一个映射关系
- 将该映射关系保存到RequestMappingInfoHandlerMapping中的pathLookup和registry两个Map中。
- pathLookup是一个Map,以请求路径作为键,将对应的RequestMappingInfo对象作为值存储起来,用于后续处理请求时的查找。
- registry是一个Map,以RequestMappingInfo对象作为键,将对应的HandlerMethod对象作为值存储起来,用于后续执行相应的方法。
- 遍历完所有的Handler类和方法后,初始化完成,此时已经将请求路径、请求方法和对应的HandlerMethod对象都保存起来了。
当有实际的请求进来时,RequestMappingHandlerMapping会根据请求的路径和方法,从pathLookup中查找对应的RequestMappingInfo对象。
然后,通过RequestMappingInfo对象从registry中获取对应的HandlerMethod对象,从而执行相应的方法。HandlerMethod对象是在处理请求时动态生成的,它包含了方法的相关信息,如所属的类、方法名、参数列表等。

protected void initHandlerMethods() {if(this.logger.isDebugEnabled()) {this.logger.debug("Looking for request mappings in application context: " + this.getApplicationContext());}//这里是获取应用中所有Object的bean的名字String[] beanNames = this.detectHandlerMethodsInAncestorContexts?BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.obtainApplicationContext(), Object.class):this.obtainApplicationContext().getBeanNamesForType(Object.class);String[] var2 = beanNames;int var3 = beanNames.length;//遍历这个含有应用中所有beanName的字符串数组,并得到这个beanName对应的bean的类型for(int var4 = 0; var4 < var3; ++var4) {String beanName = var2[var4];if(!beanName.startsWith("scopedTarget.")) {Class beanType = null;try {//根据这个beanName对应的beanType的类型beanType = this.obtainApplicationContext().getType(beanName);} catch (Throwable var8) {if(this.logger.isDebugEnabled()) {this.logger.debug("Could not resolve target class for bean with name \'" + beanName + "\'", var8);}}//判断这个根据这个bean的类型判断是不是一个handlerif(beanType != null && this.isHandler(beanType)) {this.detectHandlerMethods(beanName);}}}this.handlerMethodsInitialized(this.getHandlerMethods());}
1.1 isHandler()
这个Bean是否含有@Controller注解或@RequestMapping注解,如果是就表示是一个handler
* {@inheritDoc}* Expects a handler to have a type-level @{@link Controller} annotation.*/@Overrideprotected boolean isHandler(Class<?> beanType) {return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));}
1.2 detectHandlerMethods()
获取这个handler中所有requestMappinng的方法,然后循环去注册该方法与对应requestMapping信息到一个名为registry的一个HashMap中去
protected void detectHandlerMethods(Object handler) {Class handlerType = handler instanceof String?this.obtainApplicationContext().getType((String)handler):handler.getClass();if(handlerType != null) {Class userType = ClassUtils.getUserClass(handlerType);//获取这个handler中有requestMapping的方法//这个methods的Map结构为key是一个Method对象,value是一个RequestMappingInfo对象Map methods = MethodIntrospector.selectMethods(userType, (method) -> {try {return this.getMappingForMethod(method, userType);} catch (Throwable var4) {throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4);}});if(this.logger.isDebugEnabled()) {this.logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);}//循环去注册Method与RequestMappingInfo的关系methods.forEach((method, mapping) -> {Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);this.registerHandlerMethod(handler, invocableMethod, mapping);});}}
1.2.1 第一步selectMethods()
1、若这个targetType不是一个代理类,就获得它本身的类以及它的接口放入handlerTypes这么一个Set中去。
2、遍历这个handlerTypes,找到用户自己定义的方法并过滤出有requestMapping的方法,并将之塞入一个methodMap中
public static <T> Map<Method, T> selectMethods(Class<?> targetType, MethodIntrospector.MetadataLookup<T> metadataLookup) {LinkedHashMap methodMap = new LinkedHashMap();LinkedHashSet handlerTypes = new LinkedHashSet();Class specificHandlerType = null;//若这个targetType不是一个代理类,就获得它本身的类以及它的接口if(!Proxy.isProxyClass(targetType)) {specificHandlerType = ClassUtils.getUserClass(targetType);handlerTypes.add(specificHandlerType);}handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));Iterator var5 = handlerTypes.iterator();//遍历while(var5.hasNext()) {Class currentHandlerType = (Class)var5.next();Class targetClass = specificHandlerType != null?specificHandlerType:currentHandlerType;//找到用户自己定义的方法并过滤出有requestMapping的方法,并将之塞入一个methodMap中ReflectionUtils.doWithMethods(currentHandlerType, (method) -> {Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);Object result = metadataLookup.inspect(specificMethod);if(result != null) {Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);if(bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {methodMap.put(specificMethod, result);}}}, ReflectionUtils.USER_DECLARED_METHODS);}return methodMap;}
1 ReflectUtilsl.doWithMethods(Class<?> clazz, ReflectionUtils.MethodCallback, ReflectionUtils.MethodFilter)
入参1: Class<?> targetType
入参2: MethodCallback 一个方法回调
入参3: MethodFilter方法过滤器
在当前方法中主要做了3件事:
1、首先获取这个Class中所有定义的方法并且将之存入一个methods的Method数组中
2、遍历这个methods数组中的method如果这个mf方法拦截器为空或者这个method与方法拦截器mf的匹配规则对应,就回调mc.doWith方法。这个mc.doWith()就会调用回到去执行doWithMethods()的第二个lamda表达式。在这个表达式中又会继续回掉执行另一个方法。
3、后面我们还发现对这个类的父类和接口都有一个递归调用
其中这个mf方法拦截器就是这个RelectionUtils.USER_DECLARED_METHODS;顾名思义就是用户自己定义的方法,而非继承与Object类的方法什么的。
/*** 执行给定回调操作在给定类和父类(或者给定的接口或父接口)的所有匹配方法*/public static void doWithMethods(Class<?> clazz, MethodCallback mc, @Nullable MethodFilter mf) {// Keep backing up the inheritance hierarchy.// 从缓存中获取clazz的所有声明的方法,包括它的所有接口中所有默认方法;没有时就从{@code clazz}中获取,再添加到缓存中,Method[] methods = getDeclaredMethods(clazz, false);// 遍历所有方法for (Method method : methods) {// 如果mf不为null 且 method不满足mf的匹配要求if (mf != null && !mf.matches(method)) {// 跳过该methodcontinue;}try {// 对method执行回调操作mc.doWith(method);}catch (IllegalAccessException ex) {throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);}}// 如果clazz的父类不为null且(mf不是与未在{@code java.lang.Object}上声明的所有非桥接非合成方法匹配的预购建方法过滤器或者clazz的父类不为Objectif (clazz.getSuperclass() != null && (mf != USER_DECLARED_METHODS || clazz.getSuperclass() != Object.class)) {// 递归方法// 执行给定回调操作在clazz的父类的所有匹配方法, 子类和父类发生的相同命名方法将出现两次,// 子类和父类发生的相同命名方法将出现两次,除非被mf排查doWithMethods(clazz.getSuperclass(), mc, mf);}// 如果clazz是接口else if (clazz.isInterface()) {// 遍历clazz的所有接口for (Class<?> superIfc : clazz.getInterfaces()) {// 递归方法// 执行给定回调操作在superIfc的所有匹配方法, 子类和父类发生的相同命名方法将出现两次,// 子类和父类发生的相同命名方法将出现两次,除非被mf排查doWithMethods(superIfc, mc, mf);}}}
2 mc.doWith(method)–> 回调3
当执行到这个方法时会回掉执行doWithMethods()中的第二个入参即lamda表达式
method -> {Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);T result = metadataLookup.inspect(specificMethod);if (result != null) {Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {methodMap.put(specificMethod, result);}}}
3 metadataLookup.inspect(specificMethod)–> 回调1.2.1
执行到inspect()方法的时候又会继续调用MethodIntrospector.selectMethods()方法中的第二个入参数去执行第二个lamda表达式。
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,(MethodIntrospector.MetadataLookup<T>) method -> {try {return getMappingForMethod(method, userType);}catch (Throwable ex) {throw new IllegalStateException("Invalid mapping on handler class [" +userType.getName() + "]: " + method, ex);}});
4 getMappingForMethod()
最终执行到getMappingForMethod(),找到这个方法上的RequestMapping,如果这个方法上的requestMapping信息不为空的话就去照这个handler类上面的requestMapping信息然后将之合并.
最后返回一个RequestMappingInfo ;
@Override@Nullableprotected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {RequestMappingInfo info = createRequestMappingInfo(method);if (info != null) {RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);if (typeInfo != null) {info = typeInfo.combine(info);}String prefix = getPathPrefix(handlerType);if (prefix != null) {info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);}}return info;}
createRequestMappingInfo()
RequestMappingInfo 是请求映射信息的封装对象,用来确定请求的URL、请求方法、请求参数等信息
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);RequestCondition<?> condition = (element instanceof Class ?getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);}
protected RequestMappingInfo createRequestMappingInfo(RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {RequestMappingInfo.Builder builder = RequestMappingInfo.paths(resolveEmbeddedValuesInPatterns(requestMapping.path())).methods(requestMapping.method()).params(requestMapping.params()).headers(requestMapping.headers()).consumes(requestMapping.consumes()).produces(requestMapping.produces()).mappingName(requestMapping.name());if (customCondition != null) {builder.customCondition(customCondition);}return builder.options(this.config).build();}
1.2.2 第二步 registerHandlerMethod()
遍历methods注册handlerMethod
。。。。。省略selectMethods()中的代码
//循环去注册Method与RequestMappingInfo的关系methods.forEach((method, mapping) -> {Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);this.registerHandlerMethod(handler, invocableMethod, mapping);});}
1 registerHandlerMethod()
protected void registerHandlerMethod(Object handler, Method method, T mapping) {this.mappingRegistry.register(mapping, handler, method);}
2 register()
通过handler与method创建HandlerMethod对象;确保requestMapping唯一映射一个method, 最后注册requestMappingInfo与对应handlerMethod的关系。
public void register(T mapping, Object handler, Method method) {this.readWriteLock.writeLock().lock();try {//创建HandlerMethod对象,这个对象包含了handler与method的信息HandlerMethod handlerMethod = AbstractHandlerMethodMapping.this.createHandlerMethod(handler, method);//确保同一个requestMapping唯一映射一个method// 例如:url路径 /aaa/bbb 只能对应methodA 不能对应对应methodBthis.assertUniqueMethodMapping(handlerMethod, mapping);if(AbstractHandlerMethodMapping.this.logger.isInfoEnabled()) {//SpringBoot项目或者SpringMVC项目启动的时候控制台上输出的就是这个AbstractHandlerMethodMapping.this.logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);}//注册requestMapping与HandlerMethodInfo的关系this.mappingLookup.put(mapping, handlerMethod);List directUrls = this.getDirectUrls(mapping);Iterator name = directUrls.iterator();while(name.hasNext()) {String corsConfig = (String)name.next();this.urlLookup.add(corsConfig, mapping);}String name1 = null;if(AbstractHandlerMethodMapping.this.getNamingStrategy() != null) {name1 = AbstractHandlerMethodMapping.this.getNamingStrategy().getName(handlerMethod, mapping);this.addMappingName(name1, handlerMethod);}CorsConfiguration corsConfig1 = AbstractHandlerMethodMapping.this.initCorsConfiguration(handler, method, mapping);if(corsConfig1 != null) {this.corsLookup.put(handlerMethod, corsConfig1);}this.registry.put(mapping, new AbstractHandlerMethodMapping.MappingRegistration(mapping, handlerMethod, directUrls, name1));} finally {this.readWriteLock.writeLock().unlock();}}
相关文章:
SpringMVC源码分析(二)启动过程之RequestMappingHandlerMapping分析
a、http请求中的url是如何与对应Handler的即Controller对应method映射的? 在上篇中提到在SpringMVC中,初始化9大内置组件的时候其中有一个组件就是HandlerMapping,在初始化HandlerMapping的时候会加载代码中所有标注了Controller和RequestMap…...
KWin、libdrm、DRM从上到下全过程 —— drmModeAddFBxxx(7)
接前一篇文章:KWin、libdrm、DRM从上到下全过程 —— drmModeAddFBxxx(6) 上一回讲到了drm_internal_framebuffer_create函数中的framebuffer_check函数。讲解了该函数的参数检查部分中的第二部分,本回对于该函数余下部分进行解析。 为了便于理解,再次贴出framebuffer_ch…...
2023 年 Arm A-Profile 架构发展
随着人工智能 (AI) 的兴起和安全威胁的加剧,计算需求不断发展,作为世界设备核心的基础计算架构也必须不断发展。这就是为什么我们的工程团队向普遍存在的 Arm 架构添加新功能和技术,然后软件团队确保软件尽可能无缝地适应这些未来的功能和技术。 Arm架构是如何开发的 Arm …...
2023年09月 C/C++(五级)真题解析#中国电子学会#全国青少年软件编程等级考试
C/C++编程(1~8级)全部真题・点这里 Python编程(1~6级)全部真题・点这里 第1题:红与黑 有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。 时间限…...
CentOS系统/root根目录扩容(扩展逻辑卷)
具体操作步骤 1、查看本机磁盘环境挂载情况 2、添加磁盘分区 3、开始扩容 4、同步到文件系统 1、查看本机磁盘环境挂载情况 [rooticon ~]# df -lh 可以看到/dev/mapper/centos-root 路径下容量为50G,我们要给这个路径下的容量扩容:[rooticon ~]# lsblk…...
苍穹外卖(三) 员工分页及技术实现细节
2. 员工分页查询 2.1 需求分析和设计 2.1.1 产品原型 2.1.2 接口设计 2.2 代码开发 2.2.1 设计DTO类 根据请求参数进行封装 2.2.2 封装PageResult 后面所有的分页查询,统一都封装为PageResult对象。 员工信息分页查询后端返回的对象类型为: Result 2.…...
二进制部署MySQL8.0
1、下载MySQL官方包 ## 下载MySQL [rootlocalhost ~]# wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.20-linux-glibc2.12-x86_64.tar.xz2、解压并移动安装包 # 解压安装包 [rootlocalhost ~]# tar xf mysql-8.0.20-linux-glibc2.12-x86_64.tar.xz# 移动 mv…...
全力以赴,火山引擎边缘云代表团出战亚运会
END 未来,火山引擎边缘云赛事阵容将继续全力以赴,通过领先、可信赖的云和智能技术,助力游戏行业呈现更加精彩的竞技赛事。...
WPF页面向后端传参
WPF页面(前端)向后端传参 1、编写一个Button,绑定后端命令,并传递参数: <ButtonWidth"100"Command"{Binding SendCommand}"CommandParameter"{Binding ElementNameSendMessage, PathTex…...
PyTorch 入门
一、说明 深度学习是机器学习的一个分支,其中编写的算法模仿人脑的功能。深度学习中最常用的库是 Tensorflow 和 PyTorch。由于有各种可用的深度学习框架,人们可能想知道何时使用 PyTorch。以下是人们更喜欢使用 Pytorch 来完成特定任务的原因。 Pytorch…...
微信自动批量添加好友的方法
在现在的营销中微信已成为一种重要的沟通方式。微信目前是没有自动批量添加好友的功能,需要运营者一个一个手动去添加,这样太过于浪费时间,并且加频繁了还容易被封号,今天给大家介绍几种手动批量加好友的方式以及怎么借助第三方软…...
[网鼎杯 2018]Comment git泄露 / 恢复 二次注入 .DS_Store bash_history文件查看
首先我们看到账号密码有提示了 我们bp爆破一下 我首先对数字爆破 因为全字符的话太多了 爆出来了哦 所以账号密码也出来了 zhangwei zhangwei666 没有什么用啊 扫一下吧 有git git泄露 那泄露看看 真有 <?php include "mysql.php"; session_start(); if(…...
生态兼容性进一步提升!白鲸开源 WhaleStudio 与火山引擎ByteHouse完成产品互认
数据作为新型生产要素,已快速融入生产、分配、流通、消费和社会服务管理等各环节,深刻改变着生产方式、生活方式和治理方式。越来越多企业也在尝试充分利用数据要素,开辟全新发展路径,进一步实现业务价值提升。 在数字化转型的大…...
iOS 内存管理和优化
对内存管理和拓展有独特的描述 iOS学习-内存管理 比较详细说明内存的关系 iOS 内存管理机制与原理 iOS 内存泄漏排查方法及原因分析 对weak的实现原理描写详细 【iOS】—— weak的基本原理 iOS copy & mutableCopy iOS 深拷贝与浅拷贝 对iOS的浅复制和深复制的深入解释…...
常见工具指令【Vim | GIT | ZIP | UNZIP | IDEA】
VIM 快捷键说明Ctrl U (up)向上翻动半页Ctrl B (back)向上翻动一页Ctrl D (down)向下翻页半页Ctrl F (forward)向下翻动一页 GIT 指令解释git init 使用指定目录作为Git仓库git add filename向资源库添加文件filenamegit rm file从资源库中删除文件git branch 分支名称创…...
中国人民大学与加拿大女王大学金融硕士——顺势而为,掌握人生的方向盘
在这个快速发展的时代,每个人在不断面临全新挑战的同时, 也在时刻接收着各种机会。有人抓住时代红利,实现更新迭代;有人却只能作为旁观者,眼看时代发展,而自己被逐渐淘汰。时代从不会抛下任何一个人&#x…...
Apache Ranger:(二)对Hive集成简单使用
1.Ranger Hive-plugin安装 进入 Ranger 编译生成的目录下 找到 ranger-2.0.0-hive-plugin.tar.gz 进行解压 tar -zxvf ranger-2.0.0-hive-plugin.tar.gz -C /opt/module/ 2.修改配置文件 vim install.properties #策略管理器的url地址 POLICY_MGR_URLhttp://[ip]:6080#组件…...
【angular】实现简单的angular国际化(i18n)
文章目录 目标过程运行在TS中国际化参考 目标 实现简单的angular国际化。本博客实现中文版和法语版。 将Hello i18n!变为中文版:你好 i18n!或法语版:Bonjour l’i18n !。 过程 创建一个项目: ng new i18nDemo在集成终端中打开。 添加本地化包&#…...
Redis之主从复制,哨兵模式,集群
Redis之主从复制,哨兵模式,集群 1、主从复制1.1主从复制概述1.2Redis主从复制作用1.3Redis主从复制流程1.4部署Redis 主从复制 2、哨兵模式2.1哨兵模式原理2.2哨兵模式的作用2.3哨兵模式的结构2.4故障转移机制2.5搭建Redis 哨兵模式 3、Redis集群模式3.1…...
掌动智能浅析Web自动化测试的重要性
在现代Web开发中,确保Web应用程序的质量和稳定性至关重要。Web自动化测试工具成为了开发团队的关键资源,帮助他们自动化测试流程、减少手动劳动,提高测试覆盖率和效率。本文将介绍Web自动化测试的重要性是什么! Web自动化测试的重要性&#x…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
