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

【Spring Boot 源码学习】HttpEncodingAutoConfiguration 详解

Spring Boot 源码学习系列

在这里插入图片描述

HttpEncodingAutoConfiguration 详解

  • 引言
  • 往期内容
  • 主要内容
    • 1. CharacterEncodingFilter
    • 2. HttpEncodingAutoConfiguration
      • 2.1 加载自动配置组件
      • 2.2 过滤自动配置组件
        • 2.2.1 涉及注解
        • 2.2.2 characterEncodingFilter 方法
        • 2.2.3 localeCharsetMappingsCustomizer 方法
  • 总结

引言

前面的博文,我们从源码角度介绍了自动装配流程。虽然带大家从整体上有了清晰的认识,但是我们还不能熟练地运用。本篇就以 Spring Boot 内置的 http 编码功能为例,来带大家分析一下 HttpEncodingAutoConfiguration 的整个自动配置的过程。

在这里插入图片描述

往期内容

在开始本篇的内容介绍之前,我们先来看看往期的系列文章【有需要的朋友,欢迎关注系列专栏】:

Spring Boot 源码学习
Spring Boot 项目介绍
Spring Boot 核心运行原理介绍
【Spring Boot 源码学习】@EnableAutoConfiguration 注解
【Spring Boot 源码学习】@SpringBootApplication 注解
【Spring Boot 源码学习】走近 AutoConfigurationImportSelector
【Spring Boot 源码学习】自动装配流程源码解析(上)
【Spring Boot 源码学习】自动装配流程源码解析(下)
【Spring Boot 源码学习】深入 FilteringSpringBootCondition
【Spring Boot 源码学习】OnClassCondition 详解
【Spring Boot 源码学习】OnBeanCondition 详解
【Spring Boot 源码学习】OnWebApplicationCondition 详解
【Spring Boot 源码学习】@Conditional 条件注解

主要内容

1. CharacterEncodingFilter

在传统的 web 项目中,Springweb 开发提供的一个过滤器【即 org.springframework.web.filter.CharacterEncodingFilter 】,用来防止 web 开发中出现的乱码问题,它是 Spring 通过在 web 请求中定义 requestresponse 的编码来实现。

web.xml 中的配置示例如下:

	<filter>  <filter-name>encodingFilter</filter-name>  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  <init-param>  <param-name>encoding</param-name>  <param-value>UTF-8</param-value>  </init-param>  <init-param>  <param-name>forceEncoding</param-name>  <param-value>true</param-value>  </init-param>  </filter>

2. HttpEncodingAutoConfiguration

那么在 Spring Boot 是如何实现的呢?

Spring Boot 是通过内置的 HttpEncodingAutoConfiguration 配置类来完成这一功能。下面我们具体分析一下:

注意: 以下涉及 Spring Boot 源码 均来自版本 2.7.9,其他版本有所出入,可自行查看源码。

2.1 加载自动配置组件

从之前的《【Spring Boot 源码学习】自动装配流程源码解析(上)》中,我们知道 Spring Boot 内部针对自动配置类,会读取如下两个配置文件:

  • META-INF/spring.factories
  • META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

在这里插入图片描述

实际上 在 Spring Boot 2.7.9 版本中, Spring Boot 自己内部的 META-INF/spring.factories 中有关自动配置的注册类的配置信息已经被去除掉了,不过其他外围的 jar 中可能有自己的 META-INF/spring.factories 文件,它里面也有关于自动配置注册类的配置信息;

而 Spring Boot 内置的 HttpEncodingAutoConfiguration 配置类,则是配置在上述的第二个配置文件 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中。

在这里插入图片描述

2.2 过滤自动配置组件

上述自动配置加载完之后,就来到了 《【Spring Boot 源码学习】自动装配流程源码解析(下)》 介绍的 过滤自动配置组件 逻辑。

这部分数据对应的配置内容在 META-INF/spring-autoconfigure-metadata.properties 文件中:

org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration=
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration.ConditionalOnClass=org.springframework.web.filter.CharacterEncodingFilter
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration.ConditionalOnWebApplication=SERVLET

显然这里涉及到了 ConditionalOnClassConditionalOnWebApplication 注解,我们翻看 HttpEncodingAutoConfiguration 配置类的源码,如下:

@AutoConfiguration
@EnableConfigurationProperties(ServerProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {private final Encoding properties;public HttpEncodingAutoConfiguration(ServerProperties properties) {this.properties = properties.getServlet().getEncoding();}@Bean@ConditionalOnMissingBeanpublic CharacterEncodingFilter characterEncodingFilter() {//  。。。}@Beanpublic LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {// 。。。}// ...
}
2.2.1 涉及注解

我们先来看看上述 HttpEncodingAutoConfiguration 配置类涉及到的注解,如下:

  • @AutoConfiguration : 该类是一个自动配置类,Spring Boot 会根据项目中的依赖自动配置这个类的实例。
  • @EnableConfigurationProperties(ServerProperties.class) :启用 ServerProperties 类的配置属性,这样在配置文件中就可以使用 server.servlet.encoding 属性来配置字符编码。
  • @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) :该配置类只有在基于 servletweb 应用程序中才会被实例化。
  • @ConditionalOnClass(CharacterEncodingFilter.class) :只有在项目中存在 CharacterEncodingFilter 类时才会生效。
  • @ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true) :只有在配置文件中 server.servlet.encoding 属性的值为 "enabled" 时才会生效;当然如果配置文件中没有这个属性,也默认会生效。
  • @Bean :用于声明一个方法创建的对象是一个 Spring 管理的 BeanSpring 容器会自动管理这个 Bean 的生命周期,包括依赖注入、初始化和销毁等。
  • @ConditionalOnMissingBean :只有在当前 Spring 容器中不存在指定类型的 Bean 时,才会执行被注解的方法。这样可以用于确保在需要的时候才创建某个 Bean,避免重复创建。

其中 ServerProperties 类的属性值对应着 application.ymlapplication.properties 中的配置,通过注解@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) 实现的属性注入。

有关属性注入的内容后续笔者会另外介绍,我们先来看看ServerProperties 类相关的部分源码 和 对应的配置参数:

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {// 。。。private final Servlet servlet = new Servlet();// 。。。public static class Servlet {// 。。。@NestedConfigurationPropertyprivate final Encoding encoding = new Encoding();// 。。。}// 。。。
}public class Encoding {// 默认的HTTP编码,用于Servlet应用程序。public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;// HTTP请求和响应的字符集。如果未显式设置,将添加到"Content-Type"头中private Charset charset = DEFAULT_CHARSET;// 是否强制在HTTP请求和响应上使用配置的字符集的标志private Boolean force;// 是否强制在HTTP请求上使用配置的字符集的标志。当"force"未指定时,默认为true。private Boolean forceRequest;// 是否强制在HTTP响应上使用配置的字符集的标志。private Boolean forceResponse;// 将区域设置映射到字符集以进行响应编码的映射。private Map<Locale, Charset> mapping;// 。。。
}

当然在 application.properties 中,我们就可以添加如下的配置:

server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
# server.servlet.encoding.force-request=true 
# ...其他配置省略

注意: server.servlet.encoding.force=trueserver.servlet.encoding.force-request=true 这两个配置项实际上具有相同的功能,它们都决定是否强制对客户端请求进行字符编码。当这些配置项设置为 true时,服务器将要求客户端发送的请求内容使用指定的字符集进行编码。
另外,从 Spring Boot 2.3.5 版本开始,server.servlet.encoding.enabled 配置项已被弃用。因此,推荐的做法是直接设置 server.servlet.encoding.charset 来指定字符集,然后通过设置server.servlet.encoding.force=true 来开启对请求/响应的编码集强制控制。

2.2.2 characterEncodingFilter 方法

先来看看 characterEncodingFilter 方法的源码【】:

public CharacterEncodingFilter characterEncodingFilter() {CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();filter.setEncoding(this.properties.getCharset().name());filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));return filter;
}

上述逻辑很好理解:

  • 首先,新建一个 CharacterEncodingFilter 的实例对象 filter

  • 然后,设置 filterencoding 属性,即编码属性。其中 this.properties.getCharset().name() 就是上述 application.properties 中的 server.servlet.encoding.charset=UTF-8;如果没有配置,则默认是 UTF-8【可查看上述 Encoding 类】。

  • 接着,设置 filterforceRequestEncodingforceResponseEncoding 属性。我们来直接查 Encoding 类的 shouldForce 即可:

    public boolean shouldForce(Type type) {// Http请求,则取 server.servlet.encoding.force-request 配置// Http响应,则取 server.servlet.encoding.force-response 配置Boolean force = (type != Type.REQUEST) ? this.forceResponse : this.forceRequest;// 如果上述配置都没有if (force == null) {// 取 server.servlet.encoding.force 配置force = this.force;}if (force == null) {// 当 server.servlet.encoding.force 配置也未指定时,// 默认 强制在HTTP请求上使用配置的字符集。force = (type == Type.REQUEST);}return force;
    }
    
2.2.3 localeCharsetMappingsCustomizer 方法

话不多说,直接来看相关的源码【Spring Boot 2.7.9

	@Beanpublic LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {return new LocaleCharsetMappingsCustomizer(this.properties);}static class LocaleCharsetMappingsCustomizerimplements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {private final Encoding properties;LocaleCharsetMappingsCustomizer(Encoding properties) {this.properties = properties;}@Overridepublic void customize(ConfigurableServletWebServerFactory factory) {if (this.properties.getMapping() != null) {factory.setLocaleCharsetMappings(this.properties.getMapping());}}@Overridepublic int getOrder() {return 0;}}

上述 LocaleCharsetMappingsCustomizer 静态内部类实现了 WebServerFactoryCustomizer 接口,该接口用于自定义 Web 服务器工厂的策略接口。此类类型的任何 bean 将在服务器本身启动之前获得与服务器工厂的回调,因此您可以设置端口、地址、错误页面等。

注意:对此类接口的调用通常由WebServerFactoryCustomizerBeanPostProcessor 执行,它是一个BeanPostProcessor(在 ApplicationContext 生命周期中非常早期)。可能更安全的做法是在包含 BeanFactory 中延迟查找依赖项,而不是使用@Autowired 注入它们。

LocaleCharsetMappingsCustomizer 类实现的 customize 方法,则用于设置自定义字符编码映射,这就不得不提 server.servlet.encoding.mapping 配置属性。

默认情况下,Spring Boot 会根据请求头的 Accept-Charset 来设置响应的字符编码。但是,有时候我们可能需要根据不同的请求路径或请求参数来进行不同的字符编码映射。这时,就可以使用 server.servlet.encoding.mapping 来实现自定义的字符编码映射。

# 当请求路径以 /en/ 开头时,将字符编码设置为 UTF-8;当请求路径以 /zh/ 开头时,将字符编码设置为 GBK。
server.servlet.encoding.mapping=/en/**=UTF-8,/zh/**=GBK

注意: server.servlet.encoding.mapping 的配置优先级高于 server.servlet.encoding.charsetserver.servlet.encoding.force。因此,如果同时存在多个配置项,server.servlet.encoding.mapping 会覆盖其他配置项。

总结

本篇我们以 Spring Boot 内置的 http 编码功能为例来分析一下整个自动配置的过程,深入讲解了 HttpEncodingAutoConfiguration 配置类的相关内容。相信大家后续在看其他配置类,也能知其所以然了。

相关文章:

【Spring Boot 源码学习】HttpEncodingAutoConfiguration 详解

Spring Boot 源码学习系列 HttpEncodingAutoConfiguration 详解 引言往期内容主要内容1. CharacterEncodingFilter2. HttpEncodingAutoConfiguration2.1 加载自动配置组件2.2 过滤自动配置组件2.2.1 涉及注解2.2.2 characterEncodingFilter 方法2.2.3 localeCharsetMappingsCus…...

uni-app--》基于小程序开发的电商平台项目实战(七)完结篇

&#x1f3cd;️作者简介&#xff1a;大家好&#xff0c;我是亦世凡华、渴望知识储备自己的一名在校大学生 &#x1f6f5;个人主页&#xff1a;亦世凡华、 &#x1f6fa;系列专栏&#xff1a;uni-app &#x1f6b2;座右铭&#xff1a;人生亦可燃烧&#xff0c;亦可腐败&#xf…...

手写banner切换方式

<template><!-- banner轮播切换 --><div class"banner-wrapper"><div class"banner-info"><ul class"box" ref"box"><li v-for"(item, index) in bannerList" :key"index">&…...

技术文档工具『Writerside』抢鲜体验

前言 2023 年 10 月 16 日&#xff0c;JetBrains 宣布以早期访问状态推出 Writerside&#xff0c;基于 IntelliJ 平台的 JetBrains IDE&#xff0c;开发人员可使用它编写、构建、测试和发布技术文档&#xff0c;可以作为 JetBrains IDE 中的插件使用&#xff0c;也可以作为独立…...

Centos磁盘爆满_openEuler系统磁盘爆满清理方法---Linux工作笔记060

磁盘爆满,监控部门就会报警,报警就要处理,但是程序员并不擅长做运维的工作,记录一下把...以后用到会方便: 使用df -h命令可以看到,对应的磁盘占用情况,这里我的/dev/mapper/openeuler-root这个目录 占用的磁盘比较多,到了百分之95了.. 往往就是这个跟目录,我这里/data目录是自…...

dubbo启动提示端口号已经被占用

本地dubbo项目启动提示&#xff1a; java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132) at org.sp…...

LeetCode每日一题——2678. Number of Senior Citizens

文章目录 一、题目二、题解 一、题目 You are given a 0-indexed array of strings details. Each element of details provides information about a given passenger compressed into a string of length 15. The system is such that: The first ten characters consist o…...

按摩 推拿上门服务小程序源码 家政上门服务系统源码

按摩 推拿上门服务小程序源码 家政上门服务系统源码 上门服务系统是一款基于互联网和移动应用的高端家政服务预订平台&#xff0c;它集成了用户、服务员、客户三方的需求于一体&#xff0c;为广大市民提供方便、高效、安全、舒适的家居服务体验&#xff0c;让你在家当皇帝&…...

排列排序问题---2023 年华中科技大学程序设计竞赛新生赛

解析&#xff1a; 将序列分为多段&#xff0c;每段必须连续并且单调&#xff0c;输出段数-1即可 #include<bits/stdc.h> using namespace std; #define int long long const int N1e65; int n,a[N]; signed main(){scanf("%lld",&n);for(int i1;i<n;i)…...

数据恢复怎么做?记好这款堪比数据恢复专家的软件!

“我真的受够了数据总是莫名其妙丢失了&#xff01;但是我的电脑知识又很有限&#xff0c;文件丢失后我都不知道应该采取什么方法来进行恢复。谁能给我介绍一些方法呀&#xff1f;” 数据丢失是一场噩梦&#xff0c;无论是因为误删除、硬盘损坏、病毒攻击还是其他原因。然而&am…...

远程监控高并发高吞吐java进程

文章目录 背景工具jconsole和jvisualvm 压测实战以太坊Java程序监控1.使用jconsole监控2.使用jvisualvm监控 问题分析堆内存使用异常通过调整内存策略来应对&#xff1a; 交易虚增问题 背景 作为使用java技术栈的金融类公司&#xff0c;确保Java程序在生产环境中的稳定性和性能…...

MapperStruct实现类为空

​ 问题描述&#xff1a; MapperStruct生成的实现了为空 按照在MapperStruct官网Installation – MapStruct中的方法配置后&#xff0c;生成的实现了是空的&#xff0c;如下&#xff1a; Overridepublic DeployHistory toEntity(DeployHistoryDto arg0) {if ( arg0 null ) …...

【webpack】wabpack5 知识梳理

1、简单介绍 默认功能 可处理 js、json文件&#xff0c;处理 js 文件引入将其打包&#xff1b; 可处理字体、图片、音视频等静态资源&#xff08;webpack5有内置loader&#xff1a;asset&#xff09;&#xff1b; 将es6的import规范编译为浏览器可识别的commonjs规范&#xf…...

ThinkPHP 3.2 常用内置函数

ThinkPHP 3.2 内置函数CDM疑问&#xff1a; D与M方法的相同点与不同点IAR 内置函数 C C方法是用于获取或修改&#xff0c;系统配置参数 语法&#xff1a; 获取&#xff1a;C&#xff08;需要获得的配置参数Name&#xff09; $value C(config_name);设置&#xff1a;C&…...

STM32F4_USB读卡器(USB_Slave)/USB U盘(Host)

前言 STM32F4芯片自带了USB OTG FS&#xff08;FS&#xff0c;即全速&#xff0c;12Mbps&#xff09;和USB OTG HS&#xff0c;支持USB Host和USB Device。 1. USB简介 USB&#xff0c;是英文Universal Serial BUS&#xff08;通用串行总线&#xff09;的缩写&#xff0c;是一…...

【网络安全入门】学习网络安全必须知道的100 个网络基础知识

前言 先领取资料再阅读哦 【282G】网络安全&黑客技术零基础到进阶全套学习大礼包&#xff08;附面试题答案&#xff09;&#xff0c;免费分享&#xff01; 【282G】网络安全&黑客技术零基础到进阶全套学习大礼包&#xff08;附面试题答案&#xff09;&#xff0c;免…...

96核的AMD锐龙Threadripper PRO 7995WX性能如何?

AMD新推出的锐龙Threadripper 7000系列可以说是目前最快的工作站处理器&#xff0c;最顶级的锐龙Threadripper PRO 7995WX拥有96个Zen 4内核&#xff0c;共192线程&#xff0c;基础频率2.5GHz&#xff0c;加速频率5.15GHz&#xff0c;拥有384MB L3缓存和多达128条PCI-E 5.0通道…...

TS和JS的区别

1.TS和JS的区别 ts 是js的超集。 从执行环境上来看&#xff0c;浏览器、node.js 可以直接执行js,但不能执行ts;编译层面&#xff0c;Ts 有编译阶段&#xff0c;js 没有&#xff0c;只有转译阶段和lint阶段&#xff1b;ts更难写一点&#xff0c;但类型更安全。ts 代码写出来就是…...

顺序栈的实现----数据结构

栈的概念 对于栈&#xff08;Stack&#xff09;&#xff0c;后进先出&#xff08;Last In First Out&#xff0c;LIFO&#xff09;&#xff0c;栈也是一种线性表&#xff0c;只不过是一种操作受限的线性表&#xff0c;只能在一端操作&#xff0c;也就是不允许在中间进行查找、…...

k8s calico 网络原理

一、cluster ip Cluster IP 是 Kubernetes 中 Service 的 IP 地址&#xff0c;它是一个虚拟 IP 地址&#xff0c;用于集群内的 Pod 相互通信。 例如&#xff1a; Cluster IP&#xff1a;2.2.2.2负载的真实Pod IP&#xff1a;1.1.1.1 场景&#xff1a; Pod A 的 IP 地址是 …...

STM32F1xx HAL库 + FreeRTOS实战:构建带日志输出的交互式Shell终端

1. 为什么需要交互式Shell终端 在嵌入式开发中&#xff0c;调试手段往往决定了开发效率。想象一下&#xff0c;当你的设备在实验室运行良好&#xff0c;到了现场却出现偶发性故障&#xff0c;传统的LED灯调试方式就像在黑夜里用手电筒找钥匙 - 效率低下且信息有限。而基于STM32…...

手把手教你:在MounRiver Studio里为WCH RISC-V芯片切换GCC12工具链(附内存占用对比)

在MounRiver Studio中为WCH RISC-V芯片升级GCC12工具链的完整指南 当你第一次打开MounRiver Studio&#xff08;MRS&#xff09;并创建一个WCH RISC-V MCU工程时&#xff0c;系统默认会使用GCC8工具链进行编译。但你可能已经听说&#xff0c;新版的GCC12能带来更好的代码优化效…...

CodeChecker API开发指南:构建自定义分析工具和集成方案

CodeChecker API开发指南&#xff1a;构建自定义分析工具和集成方案 【免费下载链接】codechecker CodeChecker is an analyzer tooling, defect database and viewer extension for static and dynamic analyzer tools. 项目地址: https://gitcode.com/gh_mirrors/co/codech…...

伪代码示例:模拟PLC配置

PLC交通灯控制&#xff0c;博途V15&#xff0c;S7-1200 使用比较指令&#xff0c;程序完整&#xff0c;触摸屏调试正常&#xff0c;触摸屏上有倒计时显示功能。 有两份对应实训报告(设计说明书&#xff09;&#xff0c;包括每段程序原理解释&#xff0c;触摸屏设置过程&#xf…...

模块化多电平变换器MMC的NLM与CPS-PWM调制策略仿真实现(交流3000V-直流5000...

模块化多电平变换器MMC两种调制策略实现&#xff08;交流3000V-直流5000V整流&#xff09;仿真&#xff0c;单桥臂二十子模块&#xff0c;分别采用最近电平逼近NLM与载波移相调制CPS-PWM实现&#xff0c;仿真中使用环流抑制&#xff0c;NLM中采用快速排序&#xff0c;两个仿真动…...

高性能客服系统技术内幕:通过 SpinWait 自旋等待结构体提升高频消息分发性能骄

1. 智能软件工程的范式转移&#xff1a;从库集成到原生框架演进 在生成式人工智能&#xff08;Generative AI&#xff09;从单纯的文本生成向具备自主规划与执行能力的“代理化&#xff08;Agentic&#xff09;”系统跨越的过程中&#xff0c;.NET 生态系统正在经历一场自该平台…...

AI音频分离工具Spleeter终极指南:免费提取人声和乐器的完整教程

AI音频分离工具Spleeter终极指南&#xff1a;免费提取人声和乐器的完整教程 【免费下载链接】spleeter Deezer source separation library including pretrained models. 项目地址: https://gitcode.com/gh_mirrors/sp/spleeter 你是否曾经想要从一首歌曲中提取纯净的人…...

Discord注册新思路:不用折腾手机号,先用邮箱和桌面网页版搞定一切

Discord高效注册指南&#xff1a;巧用邮箱与网页版绕过初始验证困扰 Discord作为全球领先的即时通讯平台&#xff0c;早已突破游戏社区的边界&#xff0c;成为跨领域协作与社交的重要工具。然而对于新用户而言&#xff0c;繁琐的注册流程往往成为体验的第一道门槛。本文将揭示…...

WebExtensions性能优化终极指南:让你的浏览器扩展运行如飞

WebExtensions性能优化终极指南&#xff1a;让你的浏览器扩展运行如飞 【免费下载链接】webextensions-examples Example Firefox add-ons created using the WebExtensions API 项目地址: https://gitcode.com/gh_mirrors/we/webextensions-examples GitHub 加速计划 /…...

YOLOv10跨平台部署指南:3分钟极速安装与实战验证

YOLOv10跨平台部署指南&#xff1a;3分钟极速安装与实战验证 【免费下载链接】yolov10 YOLOv10: Real-Time End-to-End Object Detection [NeurIPS 2024] 项目地址: https://gitcode.com/GitHub_Trending/yo/yolov10 还在为深度学习环境配置而头疼吗&#xff1f;CUDA版…...