SpringBoot2.7升级项目到Springboot3.1踩坑指南(jdk17)

文章目录

    • 概要
    • 国内顶级开源项目升级情况
    • 适配SpringBoot3指南
    • SpringBoot3升级要点
      • 1、jdk17变动(如javax)
      • 2、redis修改spring.redis.host ===> spring.data.redis.host
      • 3、SpringCloudApplication注解被删除
      • 4、不兼容升级import java.servlet====>import jakarta.servlet
      • 5、swagger集成 弃用springfox--->springdoc不兼容升级
      • 6、动态数据源baomidou的dynamic-datasource依赖变动
      • 7、Spring Framework 6.0 中删除了对 Apache HttpClient 支持(RestTemplate受影响)
      • 8、SpringBoot3.0整合RocketMQ时出现未能加载bean文件

概要

由于SpringBoot3.x全面拥抱JDK17,兼容jdk21,jdk17乃是大势所趋。这里是从SpringBoot2.7-->SpringBoot3.1踩坑指南。
提前阅读:jdk8升级JDK17避坑指南(适用于SpringBoot2.3—SpringBoot2.7升级)

国内顶级开源项目升级情况

国内顶级开源项目升级到springBoot3情况,可以作为升级SpringBoot3的风向标。仅对比国内规模使用,落地过万企业的开源项目
参考:国内顶级开源项目:芋道、ruoyi、JeecgBoot、pig、SpringBlade功能对比

评价项/项目名yudao-cloudRuoyi-CloudRuoYi-Cloud-PlusDante CloudpigbladexJeecgBoot
官网芋道yudao-cloud 开发指南若依plus-doc.dromara.orgDante Cloudpig4cloudbladex.cn、看云-SpringBlade开发手册JeecgBoot
源码收费免费免费免费免费免费 + 收费(3999)免费 + 收费(5000)免费 + 收费(100000)
文档收费文档收费免费、视频收费文档免费、视频收费免费免费、授权收费文档收费文档免费、授权收费
githubyudao-cloudRuoYiRuoYi-Vue-PlusDante CloudpigSpringBladejeecg-boot
giteeyudao-cloudRuoYiRuoYi-Vue-PlusDante Cloud暂无SpringBladejeecg-boot
jdk17分支master-jdk21RuoYi-Cloud-Plus 2.Xdante-cloud 3.1.Xpig jdk17jeecg-boot/springboot3

适配SpringBoot3指南

  • 参考1-微信公众号-这可能是最全的SpringBoot3新版本变化了!、
  • 参考2-SpringBoot官网-Spring Boot 3.0 Release Notes、
  • 参考3-微信公众号-Swagger升级指南:Swagger2与Swagger3注解差异揭秘、
  • 参考4-微信公众号-Dante Cloud 3.2.0.0 发布,首个适配 Spring Boot 3.2版本及经验分享
  • 参考5-JeecgBoot 文档中心-升级SpringBoot3

SpringBoot3升级要点

前提说明,建议先完成springboot2.x—>springBoot2.7.x+jdk17的适配,这里升级难度会小很多。参考:文章最前面的文章。

1、jdk17变动(如javax)

详见: jdk8升级JDK17避坑指南(适用于SpringBoot2.3—SpringBoot2.7升级)

  • 模块化对反射的影响==>对系统类的反射增加了限制,需要打开限制增加jvm启动参数add-opens,自己写的类,可以正常使用反射。
  • 删除sun.misc 下的包,如sun.misc.BASE64Encoder==>java.util.Base64替换
  • 删除JAXB、soup相关==>maven仓库上面有新的maven坐标,引入新依赖即可
  • 删除javax.annotation==>maven仓库上面有新的maven坐标,引入依赖即可

2、redis修改spring.redis.host ===> spring.data.redis.host

redis配置命令空间进行了修改,需要注意。
参考:Spring Boot3.0(九):整合Redis

--- # redis 配置,注意springboot 3.x 有 data,2.x 没有 data
## spring.redis.host ===> spring.data.redis.host
spring:data:redis:host: 10.16.58.180port: 6379password: Admin123database: 6      

3、SpringCloudApplication注解被删除

使用@SpringBootApplication替换

4、不兼容升级import java.servlet====>import jakarta.servlet

servlet捐献给社区,为了避免版权问题,修改了包名,导致不兼容.

  • javax.servlet===> jakarta.servlet
  • javax.validation ===> jakarta.validation
  • javax.annotation ===> jakarta.annotation
  • javax.mail ===> jakarta.mail
  • javax.websocket ==> jakarta.websocket
 <!-- Java Servlet jakarta --><dependency><groupId>jakarta.servlet</groupId><artifactId>jakarta.servlet-api</artifactId><version>6.0.0</version></dependency>  

5、swagger集成 弃用springfox—>springdoc不兼容升级

springfox不维护了,springboot3使用springdoc,并启用openapi3.0,相关注解进行了变化。

参考:spring boot 3 整合 swagger3、
参考:Swagger升级指南:Swagger2与Swagger3注解差异揭秘、
参考:OpenApi3.0注解说明

注解作用swagger2swagger2示例swagger3-openApi3.0swagger示例替换
用于Controller@Api@Api(value = "/app/child/v2", tags = "儿童档案")@Tag@Tag(name = "/app/child/v2", description = "儿童档案")@Api(value = "User Management", description = "Operations pertaining to users")—>@Tag(name = "User Management", description = "Operations pertaining to users")@Api(tags = "小程序端Core Controller")—>@Tag(description = "小程序端Core Controller")
用于Controller接口@ApiOperation@ApiOperation(value = "新增儿童档案绑定监护人", httpMethod = "POST", produces = "application/json")@Operation @Operation(summary = "新增儿童档案绑定监护人", method = "POST")@ApiOperation(value = "根据儿童证件号码查询儿童档案", httpMethod = "POST", produces = "application/json")–>@Operation(summary = "根据儿童证件号码查询儿童档案", method = "POST")
用于Controller接口参数注解@ApiParam或@ApiImplicitParam@ApiParam("预约id,字段名:personApptId,默认无")@Parameter@Parameter(description = "预约id,字段名:personApptId,默认无", required = true) @ApiParam(value = "追溯码 形如:81900920216939751445,max=32", defaultValue = "81900920216939751445")—>@Parameter(description = "追溯码 形如:81900920216939751445,max=32", example = "81900920216939751445")
参数隐藏@ApiIgnore@ApiIgnore HttpServletRequest request///
实体字段@ApiModelProperty@ApiModelProperty("接种人员-姓名(冗),max=32")@ApiModelProperty(notes = "The database generated user ID")@Schema@Schema(description = "主键 自增")@ApiModelProperty(value—>@Schema(description@ApiModelProperty(hidden = true)–>@Schema(hidden = true@ApiModelProperty(value = "更新人员", example = "张三",hidden = true)—>@Schema(description = "更新人员", example = "张三",hidden = true)
实体类@ApiModel@ApiModel("接种人员-姓名(冗),max=32")@ApiModelProperty(notes = "The database generated user ID")@Schema@Schema (description = "根据儿童证件号码查询儿童档案")")@ApiModel(value—>@Schema(description@ApiModel(description —>@Schema(description
// idea正则替换01
import io.swagger.annotations.Api;
import io.swagger.v3.oas.annotations.tags.Tag;
@Api\(tags = "([^\"]+)", hidden = ([^\"]+), description = "([^\"]+)"\)
@Tag(name = "$1", description = "$3")@Api\(value = "([^\"]+)", tags = "([^\"]+)"\)
@Tag(name = "$1", description = "$2")@Api\(tags = "([^\"]+)", description = "([^\"]+)"\)
@Tag(name = "$1", description = "$2")@Api\(tags = "([^\"]+)"\)
@Tag(name = "$1")// idea正则替换02
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.Operation;
@ApiOperation\(value = "([^\"]+)", notes = "([^\"]+)", position = ([^\"]+)\)
@Operation(summary = "$1", description= "$2")@ApiOperation\(value = "([^\"]+)", httpMethod = "([^\"]+)", produces = "([^\"]+)"\)
@Operation(summary = "$1", method = "$2")@ApiOperation\(value = "([^\"]+)", notes = "([^\"]+)"\)
@Operation(summary = "$1", description = "$2")@ApiOperation\(value = "([^\"]+)", httpMethod = "([^\"]+)"\)
@Operation(summary = "$1", method = "$2")@ApiOperation\(value = "([^\"]+)"\)
@ApiOperation\("([^\"]+)"\)
@Operation(summary = "$1")// idea正则替换03
import io.swagger.annotations.ApiModel;
import io.swagger.v3.oas.annotations.media.Schema;@ApiModel\(value = "([^\"]+)"\)
@Schema(description = "$1")@ApiModel\(description = "([^\"]+)"\)
@Schema(description = "$1")// idea正则替换04
import io.swagger.annotations.ApiModelProperty;
'';
@ApiModelProperty\(value = "([^\"]+)", example = "([^\"]+)", required = ([^\"]+)\)
@Schema(description = "$1", example = "$2", required= $3)@ApiModelProperty\(value = "([^\"]+)", example = "([^\"]+)", hidden = ([^\"]+)\)
@Schema(description = "$1", example = "$2", hidden = $3)@ApiModelProperty\(value = "([^\"]+)", example = "([^\"]+)"\)
@Schema(description = "$1", example = "$2")@ApiModelProperty\(value = "([^\"]+)", hidden = ([^\"]+)\)
@Schema(description = "$1", hidden = $2)@ApiModelProperty\(value = "([^\"]+)"\)
@Schema(description = "$1")@ApiModelProperty\(hidden = ([^\"]+)\)
@Schema(hidden = $1)@ApiModelProperty\("([^\"]+)"\)
@Schema(description = "$1")@ApiModelProperty\(value="([^\"]+)"\)
@Schema(description = "$1")// idea正则替换05
import io.swagger.annotations.ApiParam;
import io.swagger.v3.oas.annotations.Parameter;@ApiParam\(value = "([^\"]+)", example = "([^\"]+)", defaultValue = "([^\"]+)"\)
@Parameter(name = "$1", example = "$2", description="$3")@ApiParam\(value = "([^\"]+)", defaultValue = "([^\"]+)"\)
@ApiParam\(value = "([^\"]+)", example = "([^\"]+)"\)
@Parameter(name = "$1", example = "$2")@ApiParam\(value = "([^\"]+)"\)
@ApiParam\("([^\"]+)"\)
@Parameter(name = "$1")// idea正则替换06
import springfox.documentation.annotations.ApiIgnore;
import io.swagger.v3.oas.annotations.Hidden;
@ApiIgnore
@Hidden@ApiIgnore HttpServletResponse response
@ApiIgnore HttpServletRequest request// idea正则替换07
@ApiImplicitParams
@Parameters@ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)",required = ([^\"]+), example = "([^\"]+)", paramType = "query"\)
@Parameter(name = "$1", description = "$2", required = $3, example = "$4", in = ParameterIn.QUERY)@ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", required = ([^\"]+), paramType = "query"\)
@Parameter(name = "$1", description = "$2", required = true, in = ParameterIn.QUERY)@ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", dataType = "([^\"]+)", required = ([^\"]+)\)
@Parameter(name = "$1", description = "$2", schema = @Schema(type = "$3"), required = $4)@ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", dataType = "([^\"]+)"\)
@Parameter(name = "$1", description = "$2", schema = @Schema(type = "$3"))@ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", paramType = "form"\)
@Parameter(name = "$1", description = "$2", in = ParameterIn.QUERY)@ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", dataType = "__file", paramType = "form"\)
@Parameter(name = "$1", description = "$2", example = "__file", in = ParameterIn.QUERY)@ApiImplicitParam\(value = "([^\"]+)"\)
@Parameter(description = "$2")

swaggr2实例

// Swagger2 实体类
@Getter
@Setter
@NoArgsConstructor
@ApiModel(value = "根据儿童证件号码查询儿童档案")
public class ChildGetByChildNoVo implements Serializable {@ApiModelProperty(value = "身份证")private String childNo;private Long personId;
}// Swagger2 Controller
@AllArgsConstructor
@RestController
@RequestMapping("/app/child/v2")
@Api(value = "/app/child/v2", tags = "儿童档案")
public class ChildController extends BaseController @ApiOperation(value = "根据监护人ID分页查询被监护人", httpMethod = "GET", produces = "application/json")@GetMapping("/getByPersonId")@ApiImplicitParam(value = "预约id")public AjaxResult getByPersonId(@RequestParam(value = "pageNum",required = false,defaultValue = "1") Integer pageNum,@ApiParam("预约id,字段名:personApptId,默认无") @RequestParam(value = "personApptId") Long personApptIdpersonId,@ApiIgnore HttpServletRequest request){IPage<ApptChildRelationship> page = new Page<>(pageNum,pageSize);IPage<ChildApiVo> pageData = childService.getByPersonId(page,personId);TableDataInfo rspData = new TableDataInfo();rspData.setCode(200);rspData.setRows(pageData.getRecords());rspData.setMsg("查询成功");rspData.setTotal(pageData.getTotal());return AjaxResult.success(rspData);}@PostMapping("/getCode")@Operation(summary = "获取预约码", method = "POST")@ApiImplicitParam(value = "预约id")@ApiImplicitParams({@ApiImplicitParam(name = "personId", value = "用户id", paramType = "form"),@ApiImplicitParam(name = "apptId", value = "预约id", paramType = "form"),@ApiImplicitParam(name = "collectLocationId", value = "采样点id", paramType = "form"),})public String getCode(Long personId, Long apptId, Long collectLocationId) {return vficPersonService.generateApptQrCode(apptId, personId, collectLocationId);}
}

swagger3示例


6、动态数据源baomidou的dynamic-datasource依赖变动

经过测试,该条也可以不升级
参考1:baomidou dynamic-datasource、
参考2:kancloud tracy5546 dynamic-datasource

<!-- spring-boot 1.5.x 2.x.x 3.xx -->
<dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>${version}</version>
</dependency><!-- spring-boot 3.x.x -->
<dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot3-starter</artifactId><version>${version}</version>
</dependency>

7、Spring Framework 6.0 中删除了对 Apache HttpClient 支持(RestTemplate受影响)

升级到SpringBoot3发现依赖提示缺少:rg.apache.httpcomponents:httpclient,一些三方库可能依赖httpclient,就需要自己手工引入依赖。三方包如:spring-data-elasticsearch5、nacos-client 1.4.6、weixin-java-pay 4.0.0、htmlunit 3.6

参考: Spring-Boot-3.0-Migration-Guide#apache-httpclient-in-resttemplate、

<!-- spring framwork 5.x中默认引入apcache httpclient4,在spring framwork6.x中如果需要要手工引入 -->
<dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.14</version>
</dependency><!-- spring framwork 6.x中的apcache httpclient5 -->
<dependency><groupId>org.apache.httpcomponents.client5</groupId><artifactId>httpclient5</artifactId><version>5.3</version>
</dependency>

8、SpringBoot3.0整合RocketMQ时出现未能加载bean文件

springboot2.x使用rocketmq没有问题,springboot3出现,required a bean of type ‘org.apache.rocketmq.spring.core.RocketMQTemplate’ that could not be found.

参考:SpringBoot3.0整合RocketMQ时出现未能加载bean文件

<dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><!--    目前也没有新版本,roketmq2.2.3没有适配springboot3--><version>2.2.3</version></dependency>
``
### 9、org.elasticsearch:elasticsearch:jar:unknown was not found 
> 从springboot2升级到springboot3 elasticsearch依赖出现问题
### 10、springboot 3.2 openFeign加载失败暂未解决 (3.1没问题)
> 启动失败 feign导致 not annotated with HTTP method type (ex. GET, POST)- https://blog.csdn.net/nailsoul/article/details/105223740- https://www.jianshu.com/p/11b4cbc8951a- https://juejin.cn/post/7112414513550491656
## 11、hutool5.8-->hutool6.0升级要要点- `ServletUtil`-->`JakartaServletUtil`- 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/608132.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ISIS基本概率与配置(HCIP完整版)

目录 一、ISIS协议基础 1、ISIS概述&#xff08;认识ISIS&#xff09; 2、ISIS的应用 4、ISIS的工作过程 5、ISIS路由器的类型 6、ISIS区域 7、ISIS报文 8、ISIS基础配置 9、进程号&#xff1a; 10、NET地址 11、ISIS邻居关系 二、邻居表分析 1、ISIS邻居表字段解析…

Oracle11.2.0.4从RMAN备份中快速恢复单个表的方法

文章目录 前言一、查询所要恢复的表所涉及的表空间二、创建用于恢复的数据库三、恢复步骤1.恢复控制文件2.修改redo日志名称3.表空间恢复4.表空间recover5.查询数据 前言 由于用户误操作导致某表中的数据错乱&#xff0c;导致业务不能正常使用&#xff0c;现需要将该表恢复到一…

【FPGA】分享一些FPGA数字信号处理相关的书籍

在做FPGA工程师的这些年&#xff0c;买过好多书&#xff0c;也看过好多书&#xff0c;分享一下。 后续会慢慢的补充书评。 【FPGA】分享一些FPGA入门学习的书籍【FPGA】分享一些FPGA协同MATLAB开发的书籍 【FPGA】分享一些FPGA视频图像处理相关的书籍 【FPGA】分享一些FPGA高速…

Java language programming:斐波那契数列(递归法)

题目&#xff1a;运用递归法求斐波那契数列。 输入格式&#xff1a; 3 输出格式&#xff1a; 2 import java.util.Scanner; public class Main {public static void main(String[] args) {Scanner inputnew Scanner(System.in);int numinput.nextInt();System.out.println(da…

更改邮箱发件人

更改邮箱发件人 未更改前发件人显示为发件人的邮箱地址 这里以outlook邮箱为例&#xff0c;进行邮箱发件人的更改 1.点击左上角“文件”选项 2.打开“账户设置”下拉菜单中的“账户设置” 3.选择“电子邮件”&#xff0c;点击该栏下的“更改”选项 4.在弹出页面中修改你…

机器学习原理到Python代码实现之NaiveBayes【朴素贝叶斯】

Naive Bayes 朴素贝叶斯算法 该文章作为机器学习的第二篇文章&#xff0c;主要介绍的是朴素贝叶斯算法的原理和应用。学习本章内容建议对概率论中的联合概率以及先验概率、后验概率有初步的学习和掌握。 难度系数&#xff1a;⭐⭐⭐ 更多相关工作请参考&#xff1a;Github 算法…

为什么流不关闭会导致内存泄漏

引言 经常有人告诉你流用完要记得关&#xff0c;不然会导致内存泄漏&#xff0c;但你是否考虑过下面这些问题: 为什么流不关会导致内存泄漏&#xff1f;JVM不是有垃圾回收机制吗&#xff1f;这些引用我用完不就变垃圾了为什么不会被回收呢&#xff1f;流未关闭除了导致内存泄…

node的下载、安装、配置

下载&#xff1a; 官网下载&#xff1a;Node.js 左右两个都可以&#xff1a; 安装&#xff1a; 打开cmd&#xff1a; 输入以下指令&#xff0c;如果出现版本号说明安装成功 node -v npm -v 配置&#xff1a; 1、新建文件夹&#xff1a;node_cache和node_global作为npm“缓…

前端实现搜索功能

最近遇到一个需求,用户在输入框输入关键字之后,点击搜索按钮后进行搜索,如下图,选中的数据在下面,上面展现的是搜索后的数据,现在选中了2条数据: 当用户输入KET后点击搜索,搜出的结果有16条,勾选全选选中后,将选中的16条的数据加到之前已选的2条数据里,于是此时已选…

重磅!大模型框架 LangChain 首个稳定版本终于来了!

著名的大模型智能体工具&#xff0c;现在有大版本更新了。 不知不觉&#xff0c;LangChain 已经问世一年了。作为一个开源框架&#xff0c;LangChain 提供了构建基于大模型的 AI 应用所需的模块和工具&#xff0c;大大降低了 AI 应用开发的门槛&#xff0c;使得任何人都可以基于…

oracle角色管理

常用角色 CONNECT,RESOURCE,DBA,EXP_FULL_DATABASE,IMP_FULL_DATABASE 1角色可以自定义&#xff0c;语法与创建用户一样 CREATE role role1 IDENTIFIED by 123; 2授权权限给角色 --自定义角色 CREATE role role1 IDENTIFIED by 123; --授权权限给角色 GRANT create view, …

AI人工智能从业人员《自然语言及语音处理设计开发工程师》证书专项培训(第二期)通知!

工业和信息化部电子工业标准化研究院联合北京龙腾亚太教育咨询有限公司和北京龙腾智元信息技术有限公司于2024年1月成功在京举办AI人工智能从业人员《自然语言及语音处理设计开发工程师》证书专项培训第一期课程&#xff0c;所有学员成功通过考试。介于学员的良好反应&#xff…

设置flex布局的元素,其子元素宽度和超过其本身时,其宽度值未被撑起问题

如图父元素main-content设置了display:flex. 里面包含了不确定个数的子元素&#xff0c;子元素样式为&#xff1a; flex: 1; min-width: 240px;现在想获取父元素的宽度&#xff0c;发现无论子元素的个数为多少&#xff0c;父元素的宽度都是一样的大小&#xff0c;并没有被子元…

ES6---判断对象是否为{}

介绍 使用es6语法判断一个对象是否为{} 示例 使用ES6的Object.keys()方法&#xff0c;返回值是对象中属性名组成的数组 let obj {}let keys Object.keys(obj) if(keys.length){alert(对象不为{}) }else{alert(对象为{}) }代码地址 https://gitee.com/u.uu.com/js-test/b…

Docker与微服务实战(基础篇)

Docker与微服务实战&#xff08;基础篇&#xff09; 1、Docker简介2、Docker安装步骤1.Centos7及以上的版本2.卸载旧版本3.yum安装gcc相关4.安装需要的软件包5.设置stable镜像仓库【国内aliyun】6.更新yum软件包索引--以后安装更快捷7.安装Docker-Ce8.启动Docker9.测试10.卸载1…

Socket closed 异常解决方案:如何解决 JMeter 压测中的问题

问题描述 JMeter 压测时会报 java.net.SocketException: Socket closed java.net.SocketException: Socket closed at java.net.PlainSocketImpl.socketConnect(Native Method) at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) at java.ne…

Temu、Shopee、Lazada等跨境流量如何提升?买家号如何批量养号?

现在在temu、Lazada、shopee等跨境电商平台开店的商家越来越多。如果商家想让商店的产品得到更多的展示&#xff0c;流量是必不可少的&#xff0c;平台的流量入口主要有几个板块。 让我们谈谈temu、Lazada、shopee搜索流量如何提升&#xff0c;有什么方法。 有两种方法可以在短…

每日算法打卡:四平方和 day 9

文章目录 原题链接题目描述输入格式输出格式数据范围输入样例&#xff1a;输出样例&#xff1a; 题目分析示例代码二分哈希 注意 原题链接 1221. 四平方和 题目难度&#xff1a;简单 题目来源&#xff1a;第七届蓝桥杯省赛C A/B组,第七届蓝桥杯省赛Java B/C组 题目描述 四…

usb转32串口方案

方案结构图 使用usb hub芯片扩展4路usb然后再一分八路串口 USB hub 选择hub芯片注意事项&#xff1a; 目前市场上多数的USB 2.0 Hub芯片,只有内建一个Transaction Translators(STT)&#xff0c;因此 当Hub接收到如Full Speed的装置进入时&#xff0c;12Mbps的「单一」信道…

python读取csv表格数据,生成折线图

生成一个python类&#xff0c;主要解决问题&#xff0c;读取csv表格中的数据&#xff0c;生成一个折线图。 输入原数据文件路径&#xff0c;例如&#xff1a; test.csv按顺序输入需要使用到的数据列&#xff0c;例如&#xff1a;1,2,3会将输入的第一列作为X轴&#xff0c;其他…