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

编写SpringBoot的自定义starter包

starter项目

先来看一下Starter的官方解释: 

Spring Boot Starter 是一种方便的依赖管理方式,它封装了特定功能或技术栈的所有必要依赖项和配置,使得开发者可以快速地将这些功能集成到Spring Boot项目中。Spring Boot官方提供了一系列的Starters,涵盖了从Web开发到安全、数据访问等多个方面

其实Starter是一种SDK思想,基于SDK高度抽象快速构建功能块或技术块是当下企业技术部门实现技术资产快速开发的利器,基于Spring的starter自然就是最高效的方法。

starter项目结构分析

我们先来看一下mybatis-spring-boot-starter的项目结构

抛开mybatis的jdbc等逐步演化的核心功能依赖,只专注我们想要参观的starter部分可以看到,mybatis-spring-boot-starter被引入后是以两个jar的形式存在,即

  • mybatis-spring-boot-autoconfigure
  • mybatis-spring-boot-starter 

其中,mybatis-spring-boot-starter 项目中并无代码,仅仅一个pom文件指明依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot</artifactId><version>2.1.4</version></parent><artifactId>mybatis-spring-boot-starter</artifactId><name>mybatis-spring-boot-starter</name><properties><module.name>org.mybatis.spring.boot.starter</module.name></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!--mybatis-starter的代码项目--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-autoconfigure</artifactId></dependency><!-- 支撑mybatis-starter的其他依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId></dependency></dependencies>
</project>

也就是说, mybatis-starter与SpringBoot对接的主要逻辑集中在mybatis-spring-boot-autoconfigure 中,mybatis-spring-boot-starter  仅仅是个壳子,它统一管理mybatis在适配SpringBoot中的资源依赖统一管理。

根据mybatis-starter以及其他标准的start开发、SpringBoot的官方资料,我们进行自定义一个starter的形式应该是以maven的多module形式创建一个项目,该项目下包含两个module,一个是XXX-spring-boot-starter,另一个是XXX-spring-boot-autoconfigure,且XXX-spring-boot-starter 仅仅是一个只包含pom依赖的的空白项目。

编写一个自定义的starter包的流程

在之前的SpringBoot中通过自定义注解使用AOP里,我们使用AOP实现了日志监听,这里还是以这个为例子,将它改造为一个开箱即用的starter。

第一步:创建module

根据SpringBoot的官方结构,创建module项目lognote-spring-boot

<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>lognote-spring-boot</artifactId><version>1.0-SNAPSHOT</version><packaging>pom</packaging><modules><module>lognot-spring-boot-starter</module><module>lognote-spring-boot-autoconfgure</module></modules><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties></project>

lognote-spring-boot下分别包含两个module,作为公共对外的lognote-spring-boot-starter和与SpringBoot框架完成自动注入对接的lognote-spring-boot-autoconfigure 

 

第二步:管理依赖

根据上述分析mybatis的例子,对starter子module的pom进行设置,让其依赖autoconfigure,以下是lognote-spring-boot-starter的pom文件依赖

<dependencies><dependency><groupId>org.example</groupId><artifactId>lognote-spring-boot-autoconfgure</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>

对autoconfigure项目进行所需依赖引入,引入对接SpringBoot的核心依赖autoconfigure和一些常用的依赖,以下是lognote-spring-boot-autoconfigure的pom文件 :

<dependencies><!-- starter包必须引入该依赖,完成对SpringBoot的适配 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId><version>2.3.6.RELEASE</version></dependency><!--其他相关依赖--><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.30</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.8</version></dependency></dependencies>

第三步:autoconfigure项目中增加配置文件

剩下的相关功能及相关配置代码均在lognote-spring-boot-autoconfigure项目中完成

使用新建基础配置启动类:

@Configuration
@ComponentScan(basePackages = "org.example.lognote.*")
public class ApplicationConfiguration {
}

配置spring.factories文件

在resources目录下新建META-INFO目录,新建spring.factories文件,文件中指定lognote-spring-boot-autoconfigure项目的启动类路径即可:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.example.lognote.ApplicationConfiguration

配置yml文件及相关配置内容(可选)

可根据组件情况是否进行yml相关配置,这里进行是否启用日志记录的开关配置,需要在yml中增加配置参数:

#yml文件下新增配置,默认为关闭
lognote:enable: false

当被其他SpringBoot项目引用时,可在其yml文件中进行配置即可。

创建参数的实体类

@ConfigurationProperties(prefix = "lognote")
@Data
@Configuration
public class LogNoteProperties {private Boolean enable = false;
}

 创建判定条件逻辑类,可基于配置内容,决定是否注入或者使用内部相关功能,(在下面具体代码中会使用该条件判定)

public class LogNoteCondition implements Condition {@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {String able= conditionContext.getEnvironment().getProperty("lognote.enable");if(null!= able && Boolean.parseBoolean(able) ){return true;}return false;}

这里提供的是基于实现条件接口进行自定义代码的逻辑处理方式来判定条件,也可以使用:@ConditionalOnProperty等注解来实现这个诉求 

第四步:  封装功能编写代码

上述内容完成后,对于lognote-spring-boot-autoconfigure来说,直接进行相关代码的编写,就像在SpringBoot项目中一样,可以直接使用SpringBoot中的相关注解等信息,我们在starter中创建的bean以及一些对象,在starter被SpringBoot的项目引用后,会一并交由引用方的Spring上下文去管理。

 相关AOP及注解内容、逻辑细节可在 SpringBoot中通过自定义注解使用AOP中了解,这里仅仅放一下核心代码部分。

首先定义一个注解:

@Target(ElementType.METHOD) // 指定注解的适用范围
@Retention(RetentionPolicy.RUNTIME) //指定运行时
//根据条件确定该注解是否生效
@Conditional(LognoteCondition.class)
public @interface LogNote {//方法描述String desc() default "";//是否记录方法执行耗时boolean timeSpan() default true;
}

进行切面逻辑开发


@Component
@Aspect
@Slf4j(topic = "LogNote")
public class LogNoteAspect {@Around("@annotation(org.example.LogNote)")public Object aroundAdvice(ProceedingJoinPoint point) throws Throwable {MethodSignature signature = (MethodSignature) point.getSignature();//获取被调用方法Method method = signature.getMethod();//取出被调用方法的注解,方便后续使用注解中的属性ApiLog loglinstener = method.getAnnotation(ApiLog.class);log.info("----------------------method[{}]start--------------------",method.getName());log.info("方法描述:{}",loglinstener.desc());log.info("参数 :{}",point.getArgs());long startTime = System.currentTimeMillis();Object proceed = point.proceed();long endTime = System.currentTimeMillis();log.info("耗时:{}ss",endTime-startTime);log.info("----------------------method[{}] end--------------------\n",method.getName())return proceed;}
}

第五步:测试&打包

使用maven将整个lognote-spring-boot-starter项目进行打包,将该依赖引入到自己项目中,在yml中进行相关配置开启,然后进行使用:

<!-- 在相关项目中引入自定义的lognote-spring-boot-starter依赖 -->        
<dependency><groupId>org.example</groupId><artifactId>lognote-spring-boot-starter</artifactId><version>1.0-SNAPSHOT</version>
</dependency>

使用:

    @LogNote(desc = "执行Es查询")public JSONObject seachEsData(String indexName, SearchSourceBuilder searchSourceBuilder) {JSONObject resultMap = new JSONObject();.......return resultMap;}

运行效果:

2023-06-05 20:00:00 [LogNote] :--------------------method[searchESData]start---------------
2023-06-05 20:00:00 [LogNote] :方法描述:执行Es查询
2023-06-05 20:00:00 [LogNote] :参数    :{"query":{"match_all:{}","size":1,"from":0}},log
2023-06-05 20:00:00 [LogNote] :耗时    : 58ss
2023-06-05 20:00:00 [LogNote] :--------------------method[searchESData]  end---------------

补充:starter项目的规范结构

结合上面的例子,其实不难发现,starter的开发核心是与SpringBoot进行连接,包括Spring上下文的串通、SpringBoot的自动化配置串通等,其核心是在于lognote-spring-boot-autoconfigure,甚至直接进行lognote-spring-boot-configure开发,第三方直接引用lognote-spring-boot-configure都可以。亦或者只创建一个lognote-spring-boot-starter项目,把原本写在autoconfigure的代码和spring.factories配置放到里面都行,这样甚至更简便,那么官方为什么还要引导使用module的形式呢。此种方式可以参考这个项目

 其实官方的推荐是站在组件的角度去考虑,starter是以组件级为基础单位,所以采用module的方式进行开发具有更好的解耦性,可以明确层次的组件边界的概念。

使用该结构的优势主要有两点

组件开发管理规范化

使用module可以使组件的开发更加规范化,同一组件相关逻辑和内容可以集中在一个module中而不是四散的各个独立的项目。

另外,对于开发中的依赖管理也可以更加友好,例如原本在lognote-spring-boot-autoconfigure中的一堆依赖,目前仅仅是单独在autoconfigure中,如果再有新的组件扩展需要,新项目可能还要再额外进行引入依赖,这中间各个项目的版本管理以及组件本身的版本管理, 如果使用module集中管理的方式,组件相关的扩展以及核心代码都在XXX-spring-boot这个父项目中,各子包、组件所需的依赖也可以统一在一处管理,对于组件的版本、发布都是十分规范的。

例如上述例子中,可以将autoconfigure的通用依赖统一放到lognote-spring-boot这个父类的pom中统一引入管理

<modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>lognote-spring-boot</artifactId><version>1.0-SNAPSHOT</version><packaging>pom</packaging><modules><module>lognot-spring-boot-starter</module><module>lognote-spring-boot-autoconfgure</module></modules><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><!-- 公共依赖 --><dependencyManagement><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.30</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.8</version></dependency></dependencyManagement>

组件的扩展和演进更友好

XXX-spring-boot-starter作为整个组件的唯一出口,是对外交流的,所以只需要是“接口”、“封面"性质即可,autoconfigure是作为与SpringBoot的Spring上下文和自动化配置对接的一个衔接逻辑,本质也是在对接层,而核心的组件则可以以jar进行灵活开发。例如mybatis,它的核心jdbc以及mybtais相关逻辑组件,都可以独立进行开发,最后统一通过autoconfigure进行与SpringBoot进行对接即可兼容SpringBoot,日后再有其他框架,也可以在autoconfigure层进行适配。其核心部分 

相关文章:

编写SpringBoot的自定义starter包

starter项目 先来看一下Starter的官方解释&#xff1a; Spring Boot Starter 是一种方便的依赖管理方式&#xff0c;它封装了特定功能或技术栈的所有必要依赖项和配置&#xff0c;使得开发者可以快速地将这些功能集成到Spring Boot项目中。Spring Boot官方提供了一系列的Star…...

【LeetCode:3106. 满足距离约束且字典序最小的字符串 + 贪心】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…...

25 Python常用函数——reduce()

在 Python 3.x 中&#xff0c;reduce() 不是内置函数&#xff0c;而是放到了标准库 functools 中&#xff0c;需要先导入再使用。 标准库 functools 中的函数 reduce() 可以将一个接受两个参数的函数以迭代累积的方式从左到右依次作用到一个序列或迭代器对象的所有元素上&#…...

oracle登录报“ORA-27101: shared memory realm does not exist”

oracle登录报“ORA-27101: shared memory realm does not exist” 问题&#xff1a; 1、使用ip:1521/服务名方式连库报错" ORA-27101: shared memory realm does not exist Linux-x86_64 Error: 2: No such file or directory" 2、sqlplus XX/密码 可以登录数据库 …...

界面控件Telerik UI for WPF 2024 Q2亮点 - 全新的AIPrompt组件

Telerik UI for WPF拥有超过100个控件来创建美观、高性能的桌面应用程序&#xff0c;同时还能快速构建企业级办公WPF应用程序。UI for WPF支持MVVM、触摸等&#xff0c;创建的应用程序可靠且结构良好&#xff0c;非常容易维护&#xff0c;其直观的API将无缝地集成Visual Studio…...

IT服务运营过程中的资源要素管理(至简)

在IT服务运营管理过程中&#xff0c;所有资源要投入正式、连续、稳定运行&#xff0c;要保持规范化的管理和标准化的操作&#xff0c;具体包括工具管理、知识管理、服务台管理与评价、备件库管理等内容。 一、工具管理 1、工具的基本运营。见下表&#xff1a; 工具的基本运营…...

wodpress设置固定链接的方式和好处【SEO优化】

设置固定链接的好处 提高用户体验&#xff1a;固定链接使得网址更加直观和易于记忆&#xff0c;用户可以更容易地分享和访问文章。 优化SEO&#xff1a;搜索引擎更倾向于索引具有清晰结构的网址&#xff0c;固定链接有助于提高网站的SEO表现。 避免URL重复&#xff1a;固定链…...

【C#】 CancellationTokenSource 与Thread的启动、取消的区别?

1.Thread的使用 Thread的使用参考&#xff1a;【C#】Thread的使用 2.CancellationTokenSource 的使用 CancellationTokenSource在C#中用于取消长时间运行的操作&#xff0c;如异步或后台任务。它允许你从外部请求一个操作的取消&#xff0c;并且被取消的操作可以通过检查Ca…...

基于 HTML+ECharts 实现智慧运维数据可视化大屏(含源码)

智慧运维数据可视化大屏&#xff1a;基于 HTML 和 ECharts 的实现 在现代企业中&#xff0c;运维管理是确保系统稳定运行的关键环节。随着数据量的激增&#xff0c;如何高效地监控和分析运维数据成为了一个重要课题。本文将介绍如何利用 HTML 和 ECharts 实现一个智慧运维数据可…...

AIGC(Artificial Intelligence Generated Content)

随着人工智能技术的飞速发展&#xff0c;AIGC&#xff08;Artificial Intelligence Generated Content&#xff09;在各个领域的应用日益广泛&#xff0c;其中也包括前端开发的重要部分——CSS&#xff08;层叠样式表&#xff09;的优化。CSS作为网页设计中控制布局和样式的关键…...

02 MySQL数据库管理

目录 1.数据库的结构 sql语言主要由以下几部分组成 2. 数据库与表的创建和管理 1&#xff0c;创建数据库 2&#xff0c;创建表并添加数据 3&#xff0c;添加一条数据 4&#xff0c;查询数据 5&#xff0c;更新数据 6&#xff0c;删除数据 3.用户权限管理 1.创建用户 …...

C++编程: 使用 Nanomsg 进行 PUB-SUB 模式基准测试

文章目录 0. 引言1. Nanomsg简介1.1 可扩展性协议类型1.2 支持的传输机制1.3 NanoMsg 架构与实现 2. PUB-SUB 模式基准测试 0. 引言 Nanomsg 作为一款高性能的通信库&#xff0c;支持多种消息传递模式&#xff0c;其中包括 PUB-SUB&#xff08;发布-订阅&#xff09;。 本篇文…...

【Unity2D 2022:Data】读取csv格式文件的数据

一、创建csv文件 1. 打开Excel&#xff0c;创建xlsx格式文件 2. 编辑卡牌数据&#xff1a;这里共写了两类卡牌&#xff0c;第一类是灵物卡&#xff0c;具有编号、卡名、生命、攻击四个属性&#xff1b;第二类是法术卡&#xff0c;具有编号、卡名、效果三个属性。每类卡的第一…...

美团测开面经整理大汇总!!

大厂测开面经&#xff0c;加油加油&#xff0c;一周看一篇 美团测开面经美团测开暑期实习面经第二弹美团-地图服务部测开一面面经&#xff08;70min&#xff09;美团-优选事业部测开一面面经美团-优选事业部测开二面面经&#xff08;82min&#xff09;美团第一次测开笔试美团测…...

微信公众号获取用户openid(PHP版,snsapi_base模式)

微信公众号获取用户openid的接口有2个&#xff1a;snsapi_base、snsapi_userinfo 详情见微信公众号开发文档&#xff1a;https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html 本文介绍用PHP方式调用snsapi_base接口获取微信用户…...

DuckDB核心模块揭秘 | 第1期 | 向量化执行引擎之Pipeline

DuckDB核心模块揭秘 | 第1期 | 向量化执行引擎之Pipeline DuckDB是一款非常火的OLAP嵌入式数据库&#xff0c;性能超级棒。它分为多个组件&#xff1a;解析器、逻辑规划器、优化器、物理规划器、执行器以及事务和存储管理层。其中解析器原语PgSQL的解析器&#xff1b;逻辑规划器…...

Vue如何让用户通过a链接点击下载一个excel文档

在Vue中&#xff0c;通过<a>标签让用户点击下载Excel文档&#xff0c;通常需要确保服务器支持直接下载该文件&#xff0c;并且你有一个可以直接访问该文件的URL。以下是一些步骤和示例&#xff0c;展示如何在Vue应用中实现这一功能。 1. 服务器端支持 首先&#xff0c;…...

美摄科技企业级视频拍摄与编辑SDK解决方案

在数字化浪潮汹涌的今天&#xff0c;视频已成为企业传递信息、塑造品牌、连接用户不可或缺的强大媒介。为了帮助企业轻松驾驭这一视觉盛宴的制作过程&#xff0c;美摄科技凭借其在影视级非编技术领域的深厚积累&#xff0c;推出了面向企业的专业视频拍摄与编辑SDK解决方案&…...

MySQL:增删改查、临时表、授权相关示例

目录 概念 数据完整性 主键 数据类型 精确数字 近似数字 字符串 二进制字符串 日期和时间 MySQL常用语句示例 SQL结构化查询语言 显示所有数据库 显示所有表 查看指定表的结构 查询指定表的所有列 创建一个数据库 创建表和列 插入数据记录 查询数据记录 修…...

初识git工具~~上传代码到gitee仓库的方法

目录 1.背景~~其安装 2.gitee介绍 2.1新建仓库 2.2进行相关配置 3.拉取仓库 4.服务器操作 4.1克隆操作 4.2查看本地仓库 4.3代码拖到本地仓库 4.4关于git三板斧介绍 4.4.1add操作 4.4.2commit操作 4.4.3push操作 5.一些其他说明 5.1.ignore说明 5.2git log命令 …...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中&#xff0c;手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力&#xff0c;既支持点击、长按、拖拽等基础单一手势的精细控制&#xff0c;也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代&#xff0c;海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构&#xff0c;在处理大规模数据抓取任务时展现出强大的能力。然而&#xff0c;随着业务规模的不断扩大和数据抓取需求的日益复杂&#xff0c;传统…...

华为OD机试-最短木板长度-二分法(A卷,100分)

此题是一个最大化最小值的典型例题&#xff0c; 因为搜索范围是有界的&#xff0c;上界最大木板长度补充的全部木料长度&#xff0c;下界最小木板长度&#xff1b; 即left0,right10^6; 我们可以设置一个候选值x(mid)&#xff0c;将木板的长度全部都补充到x&#xff0c;如果成功…...

Vue ③-生命周期 || 脚手架

生命周期 思考&#xff1a;什么时候可以发送初始化渲染请求&#xff1f;&#xff08;越早越好&#xff09; 什么时候可以开始操作dom&#xff1f;&#xff08;至少dom得渲染出来&#xff09; Vue生命周期&#xff1a; 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...

嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)

目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 ​编辑​编辑 UDP的特征 socke函数 bind函数 recvfrom函数&#xff08;接收函数&#xff09; sendto函数&#xff08;发送函数&#xff09; 五、网络编程之 UDP 用…...

Vue3中的computer和watch

computed的写法 在页面中 <div>{{ calcNumber }}</div>script中 写法1 常用 import { computed, ref } from vue; let price ref(100);const priceAdd () > { //函数方法 price 1price.value ; }//计算属性 let calcNumber computed(() > {return ${p…...

OCR MLLM Evaluation

为什么需要评测体系&#xff1f;——背景与矛盾 ​​ 能干的事&#xff1a;​​ 看清楚发票、身份证上的字&#xff08;准确率>90%&#xff09;&#xff0c;速度飞快&#xff08;眨眼间完成&#xff09;。​​干不了的事&#xff1a;​​ 碰到复杂表格&#xff08;合并单元…...

yaml读取写入常见错误 (‘cannot represent an object‘, 117)

错误一&#xff1a;yaml.representer.RepresenterError: (‘cannot represent an object’, 117) 出现这个问题一直没找到原因&#xff0c;后面把yaml.safe_dump直接替换成yaml.dump&#xff0c;确实能保存&#xff0c;但出现乱码&#xff1a; 放弃yaml.dump&#xff0c;又切…...