推荐网站建设服务商/搜狗网址
Summary
Xata 公司在基于 PostgresSQL 开发自己的分布式数据库,出于 Zig 和 C 语言以及 PostgreSQL 的 API 有更好的互操作性的考虑,他们选择了 Zig 而非当红炸子鸡语言 Rust。他们的博客文章中对 pgzx 进行了介绍。让我们来看下他们对 Zig 和 Rust 语言的对比,以及 pgzx —— 一个支持用 Zig 语言来开发 PG 插件的框架。
Xata 是一个基于 PostgreSQL 的 Serverless 数据平台,在 PostgreSQL 之上提供全文搜索、向量相似性搜索和文件/图像等小文件存储等功能。根据官网的介绍,Xata 希望从根本上简化开发人员处理数据的方式 —— “数据库不仅仅可以在表中存储行和列,我们希望提供一站式的多模存储,并提供一个可与您喜爱的工具配合使用的互联数据平台。”
3 月 21 日,在 Xata 的发布周,Xata 的工程师发布了 pgzx 。pgzx,是一个使用 Zig 编程语言创建 Postgres 扩展的框架,提供一组基础库(例如错误处理、内存分配器、Wrapper 包装)以及构建和开发环境,简化了与 Postgres 代码库的集成。在介绍文章中,项目的主要开发者,Tudor Golubenco 和 Steffen Siering 介绍了该拓展的有趣的功能,以及他们为什么选择 Zig 来进行开发。相对于大家已经熟悉的 pgrx(基于 Rust 的创建 Postgres 扩展的框架),它有什么优势?
用 Zig 语言开发类似于 Citus 的分布式数据库
Xata 的工程师一直在做一个新的 Postgres 项目,该项目还没有真正的名称,内部称其为 “Xata 的分布式 Postgres 行动”(Xata’s take on distributed Postgres)。这个项目还处于早期阶段,但是已经有了开源计划。它与 Citus 有点相似,不过,基于过去几年运行 Xata 所学到的知识,团队做了一些不同的选择。
在项目之初,Xata 团队考虑了三种潜在的实现方向:
- 作为外部代理,类似于 Vitess 对 MySQL 的处理方式。
- 作为一个 Postgres 扩展,类似于 Citus。
- 作为 Postgres 的一个分支,类似于 Greenplum。
对第一种选项,他们显然更有经验,因为这就是目前的 Xata 代理的工作方式,所以也就很自然的倾向于这种方式。可以使用 Postgres 的网络协议,解析传入的查询并深入理解它们,并且通过两阶段提交创建分布式事务。然而,经过思考,他们发现利用现有的 Postgres 代码将会带来更多的选择,最好能复用那些非常复杂的部分。考虑到项目的长期愿景,加上也不想维护一个分支,最终决定做一个类似于 Citus 的 Postgres 拓展。
在编程语言的选择中,他们则选择了 Zig,排除了 C 和现在非常流行的 Rust。Xata 的工程师在 HackerNews 论坛上表示,“使用 Zig 的原因之一是我们可以直接复用 C API。”
Zig 的主要优势包括:
- 它几乎可以直接调用 Postgres 的 API,这样我们拥有与使用 C 相同的能力。
- 它比 C 提供更多的内存安全性,更具表现力,也更有趣。
- 它与 Postgres 代码库非常匹配,例如在内存管理和字符串处理方面。
决定了方向和语言之后,pgzx 也就应运而生了。就像用 Rust 开发 PG 插件产生了 pgrx 项目,Xata 也需要一个 Zig 版本。Zig 和 pgzx 不一定适合去开发所有的 Postgres 扩展,但他们认为它是将来这个类似于 Citus 项目的最合理选择。
介绍一下 Zig
Zig 在其网站上被描述为一种通用的编程语言和工具链,用于维护健壮、优化和可重用的软件。Zig 仍然是一种新的语言(pre 1.0),但它在系统编程社区中已经逐渐开始流行,并有网友称其为 “最赚钱的编程语言”。Zig 简化了很多东西,也因此被戏称 Rust --, 但是简化不代表弱,Zig 为 C 语言提供了一个很不错的解决方案,提供更安全的内存管理,编译时计算(comptime)以及丰富的标准库。
使用 Zig 进行 PostgreSQL 开发的一个主要原因是它能够与 C 代码进行互操作。Zig 支持 C ABI,可以与 C 指针和数据类型兼容,包括 NULL 结束的字符串,并且甚至可以将 C 头文件转换为 Zig 代码。Zig 将 C 语言的宏自动转换为 Zig 代码的功能虽然还不是很完美,但仍然很有帮助。
pgzx 这个项目,由于其使用了 Zig 语言,也有了一些有趣的新特性。并且由于语言的选择也带来了很广泛的讨论度。
pgzx 的简介
Zig 可以调用任何 C 函数并将 C 的宏转换为内联 Zig 函数,您可以按照与开发一个 PG 的 C 扩展相同的步骤用 Zig 来编写 Postgres 扩展,不需要任何其他工具。但是,pgzx 可以提供开发环境、一组用于 Postgres API 的基础库和 Wrapper 包装、常见错误处理等,这样的框架显然可以简化使用 Zig 中编写 Postgres 扩展的流程。
示例
pgzx 目前有 2 个示例扩展。
char_count_zig
, 一个很简单的扩展;- 以及
pg_audit_zig
, 更复杂并且显示了 pgzx 的更多功能。
我们看一下 char_count_zig
,它是一个功能上只比 “Hello, World!” 多一点点的 Postgres 扩展示例,添加一个函数来计算字符在字符串中出现的次数。
select char_count_zig('hi hii', 'i');char_count_zig
----------------3
(1 row)
虽然用 Zig 和 C 很相似,但 Zig 版本更简洁一些。一方面是因为 Zig 更具表现力,另外也是因为 pgzx 做了更多的工作。
注册的 SQL 函数接收序列化的参数,并且需要一些代码来进行反序列化。在 C 中,可以用 G_GETARG_* 宏来完成此操作,但是使用 pgzx,您只需将它们作为已反序列化的普通参数接收即可。如何?通过使用 comptime 函数在编译时生成必要的样板代码。如果您好奇,请查看 pgCall 函数,这是一个很好的例子,展示了 Zig 在comptime 执行的强大功能。
PG_MODULE_MAGIC
和 PG_FUNCTION_INFO_V1
函数是 comptime 使用的第二个示例。它们导出 Postgres 所需的符号,将其识别为扩展并将该函数注册为 SQL 函数。在这种情况下,comptime 的行为与对应的C语言宏非常相似。
运行时内存安全
如果你仔细看了上面的代码,就会发现它有个 bug。它检查了 target_char
不应该超过 1 个字符,但它没有检查它是否为 0 个字符。之后,代码访问 target_char[0]
时,如果字符串为空字符串,就会出现越界访问错误。我们故意留下了这个bug,这样可以看到当扩展中出现这种错误时会怎么样。
使用以下 SQL 触发此错误:
select char_count_zig('hi hii', '');
返回信息:
server closed the connection unexpectedlyThis probably means the server terminated abnormallybefore or while processing the request.
The connection to the server was lost. Attempting reset: Failed.
在 C 代码中,此类错误可能会触发 segmentfault 错误甚至安全漏洞。使用 char_count_zig
扩展尝试此操作,Postgres 进程仍然崩溃(但不是整个服务器受影响,只有提供连接的进程),但是检查日志,您将看到如下错误消息:
thread 70501513 panic: index out of bounds: index 0, len 0
/Users/tsg/src/xataio/pgzx/examples/char_count_zig/src/main.zig:21:32: 0x103aaedff in char_count_zig (char_count_zig)if (char == target_char[0]) {^
/Users/tsg/src/xataio/pgzx/src/pgzx/fmgr.zig:95:5: 0x103aaf20f in call (char_count_zig)const value = @call(.no_async, impl, callArgs) catch |e| elog.throwAsPostgresError(src, e);^
???:?:?: 0x10316045b in _ExecInterpExpr (???)
???:?:?: 0x10315fbef in _ExecInterpExprStillValid (???)
???:?:?: 0x10326ceef in _evaluate_expr (???)
???:?:?: 0x10326da67 in _simplify_function (???)
???:?:?: 0x10326bacf in _eval_const_expressions_mutator (???)
它准确指出了错误发生的位置!发生这种情况是因为 Zig 根据 Build Mode(Zig 有四种 Build Mode,Debug (default),ReleaseFast,ReleaseSafe 和 ReleaseSmall)进行运行时检查。例如,ReleaseSafe
模式会牺牲一些性能来换取更多安全检查。
请注意,这个 stacktrace 很完整,因为错误出现在 Zig 代码中。在构建 Postgres 扩展时,经常需要调用 Postgres API,如果使用不正确,仍然会出现 segmentfault 错误。
内存管理
Postgres 使用分配器 arena 来管理内存。在 Postgres 源代码中,这些 arena 被称为 memory contexts 。在一个“ contexts ”中分配的内存可以一次性释放(比如,当查询执行完成的时候),这显著简化了内存管理,因为你只需要跟踪 “contexts”,而不是单个分配。Contexts 还是分层的,因此你可以创建一个 contexts 作为另一个 contexts 的子节点,当父节点的 contexts 被释放时,所有子节点也将被释放。这样内存泄漏就几乎不可能发生。
内存 contexts 的另一个优点是它们改善了内存监控,因为 contexts 有名称,你可以看到每个 contexts 使用了多少内存。这对于调试大内存场景是很有用的。 这种使用 arena/contexts 分配器的模型恰好非常适用于 Zig。 因为 Zig 的使用习惯是让所有分配内存的函数/对象接收一个分配器作为参数。这样,分配内存的行为会更加显式,而且适合使用自定义分配器的地方。pgzx 在 Postgres 的 contexts 外面又包装了一层,这样定义的自定义内存分配器更适合被 Zig 代码调用。
以下是一个示例,创建了一个新的 contexts 作为当前 contexts 的子节点,并获取了该 contexts 的分配器:
var memctx = try pgzx.mem.createAllocSetContext("zig_context", .{ .parent = pg.CurrentMemoryContext });
const allocator = memctx.allocator();
错误处理
在更复杂的扩展中,你很可能需要了解 Postgres API 的错误处理机制。Postgres 通过 setjmp/longjmp
实现了 C 中的 "异常"处理,并提供了一组宏来抛出和捕获它们(PG_TRY/PG_CATCH
)。
问题在于长跳转可能会绕过 Zig 的控制流,比如说,errdefer
块可能没有被执行。这说明如果你的扩展调用了 Postgres API,这些 API 可能会抛出错误,长跳转可能会跳过你的 defer
和 errdefer
块!
幸运的是,pgzx 此时可以发挥作用。它提供了一组函数,允许你捕获 Postgres 的异常并将它们转换为 Zig 的错误。例如:
var errctx = pgzx.err.Context.init();
defer errctx.deinit();
if (errctx.pg_try()) {// Call Postgres C functions.
} else {return errctx.errorValue();
}
开发环境
pgzx 附带一个基于 Nix flakes 的开发环境,可以用于开发扩展和 pgzx 本身。它还附带一个项目模板,您可以使用该模板在新存储库中设置此环境。
请安装 Nix,然后运行:
mkdir my_extension
cd my_extension
nix flake init -t github:xataio/pgzx
然后加载 nix shell:
nix develop
开发环境包括许多命令,这些命令可以用于在开发环境中重新定位 Postgres 二进制文件、启动服务器等。该模板还附带一个最小的扩展和一个包含一些常见任务的 build.zig
文件。请参阅模板 README 文件,了解如何从此时开始构建扩展。
单元测试
Postgres 扩展通常使用名为 pg_regress
的工具进行测试。 pgzx 也一样,只需调用 zig build pg_regress
即可。
但我们也想进行单元测试。这有点棘手,因为测试需要在 Postgres 实例的 contexts 中编译和运行。否则,就无法与 Postgres 的 API 交互。
为了解决这个问题,pgzx 注册了一个定制的 run_tests
功能。可以通过 SQL 调用(SELECT run_tests()
) 来运行单元测试。一个测试套件是一个以 test
开头的 Zig 结构体。
要注册一个测试套件,你通常会做类似以下的操作:
comptime {pgzx.testing.registerTests(@import("build_options").testfn, .{Tests});
}
registerTests 函数是 comptime
使用的另一个用例。它会迭代结构体的所有字段,并在 SQL 中调用 run_tests()
函数时生成运行测试的调用。
其他
本文涵盖了 pgzx 已发布的一些有趣的功能,但还 pgzx 还有更多功能,比如,Postgres 数据结构的 wrapper(列表、哈希表)、SPI、共享内存访问、连接管理等等…
现状和下一步计划
pgzx 现在还是 “alpha” 阶段。但如果您想要构建 Postgres 扩展并且想要使用 Zig,使用 pgzx 会容易得多。现有功能位于README.文件中。
另外,如果这篇博文激发了您对 Zig 的兴趣并且您想尝试一下,为什么不开发一个 Postgres 扩展呢?
既生瑜,何生亮?
几乎每个程序员都为自己喜爱的语言辩护。尽管 pgzx 本身的能力很引人注目,Xata团队对未来的计划也令人兴奋,但是最引发讨论和质疑的,无疑是其选择了 Zig 作为开发语言。在 HackerNews 社区的网友也大多都关于这一点。
有网友表示,“ Zig 是一门非常令人愉快的语言。 comptime 是一项巧妙的发明。它以相同的语言语法实现类型安全的元编程和简单的通用支持。对 u2、u3 或 u7 等子字节类型的本机支持使打包数据变得非常容易。 SIMD 上的原生向量支持使得 SIMD 级并行编程就像儿童游戏一样。这种语言看起来非常好。”
也有人直接提出这个问题,难道 Rust 不是一个清楚的选择吗?Zig 跟 C 的互操作性看起来很抽象啊。对此,博文作者 Tudor 亲自下场,说,“Rust 很好啊,但是在 Zig 中,我们几乎可以直接使用任何东西,非常方便。”
还有的观点指出,“我还没有使用 pgzx,但内存管理可能更容易。 Zig 使用内存区域,Postgres 也是如此。如果一个可以直接映射到另一个,那将是一个big win。对于 Rust/pgrx(我已经广泛使用过),内存集成更加困难。有 pg 内存,有 Rust 内存,它们不一样,你必须玩游戏才能在它们之间传递数据。终生问题突然出现。 Rust 将来也许能够通过自定义分配器解决这个问题,但目前还没有实现。”
更有人直接说“当使用 Rust 处理复杂的数据结构时,经常需要编写不安全的代码(unsafe code)。Unsafe code 在 Rust 中使用起来要痛苦得多。一旦进入,无论如何你都是在不安全的地方,有人说 Zig 更方便。”
针对这些,你怎么看,欢迎加入社区跟我们一起讨论。
相关文章:

放弃 Rust 选择 Zig,Xata 团队推出 pgzx —— 计划使用 Zig 开发基于 PG 的分布式数据库
Summary Xata 公司在基于 PostgresSQL 开发自己的分布式数据库,出于 Zig 和 C 语言以及 PostgreSQL 的 API 有更好的互操作性的考虑,他们选择了 Zig 而非当红炸子鸡语言 Rust。他们的博客文章中对 pgzx 进行了介绍。让我们来看下他们对 Zig 和 Rust 语言…...

线性数据结构----(数组,链表,栈,队列,哈希表)
线性数据结构 数组链表栈使用场景 队列应用场景 哈希表特点哈希函数,哈希值,哈希冲突键值对 Entry 开放寻址法和拉链法 参考文档 数组 数组(Array) 是一种很常见的数据结构。由相同类型的元素组成,并且是使用一块连续的内存来存储的。 在数组…...

lvgl 窗口 windows lv_port_win_visual_studio 版本 已解决
不知道的东西,不知道lvgl窗口。一切从未知开始 lv_port_win_visual_studio 主分支 对应的分支 v7版本更新git submodule update --init --recursive同步 lvgl代码随后打开 visualSudio 打开.sln 文件 编译 release模式 允许 一切正常代码部分...

【多模态融合】SuperFusion 激光雷达与相机多层次融合 远距离高清地图预测 ICRA 2024
前言 本文介绍激光雷达与相机进行多层次融合,包括数据级融合、特征级融合和BEV级融合。 融合后的BEV特征可以支持不同的任务头,包括语义分割、实例编码和方向预测,最后进行后处理生成高清地图预测,它是来自ICRA 2024的。 会讲解…...

富格林:梳理正规本领远离虚假套路
富格林悉知,黄金投资者在从事黄金交易之前,必须先了解黄金交易的风险。因为投资虽然能给你带来一定的收益,但往往也有亏损的风险。在进场后投资者可通过正规经验指导有效避免因为虚假诱导带来的异常亏损,增加安全做单盈利机会。以…...

fastadmin学习01-windows下安装部署
下载源代码 官网 安装 解压,然后使用phpstorm打开 修改配置文件 创建数据库 -- drop database fastadmin01; create database fastadmin01;这样fastadmin就部署好了 访问主页也能看到前台页面...

JAVA学习-网络编程.TCP
TCP(Transmission Control Protocol)是一种面向连接的、可靠的传输协议,它在Java网络编程中被广泛应用。TCP通信可以确保数据的可靠传输,并且具有一定的顺序性。 一、Java中实现TCP通信主要有以下几种方式: 1. Socke…...

[Android]创建Google Play内购aab白包
开发时需要调试Google内购,需要先往Google商店传一个白包上去。确定包名,然后进行内购产品创建。 1.创建一个空项目,填写正式名称和正式包名。 如果你只是为一个测试开发账号打白包,然后进行内购测试,这时包名随便写…...

大数据基础:Linux基础详解
课程介绍 本课程主要通过对linux基础课程的详细讲解,让大家熟练虚拟机的安装使用,Linux系统的安装配置,学习掌握linux系统常用命令的使用,常用的软件安装方法,制作快照,克隆,完成免密登录&…...

unity中 鼠标按下移动端与pc端的位置
if (Input.GetMouseButtonDown(0)) { Vector2 V Input.touchCount > 0 ? Input.GetTouch(0).position : new Vector2(Input.mousePosition.x, Input.mousePosition.y); } 射线检测 if (Input.GetMouseButtonDown(0)) { …...

增强现实(AR)在广告中的力量
The Power of AR in Advertising 写在前面 增强现实(AR -Augmented Reality)是指借助软件、应用程序和智能手机、平板电脑或耳机等设备,为日常生活添加视觉和音频元素的技术。如今,品牌和广告商可以在营销活动中使用AR࿰…...

日志收集监控告警平台的选型思考
目前市面上比较常见的日志收集系统有:ELK,Grafana Loki,OpenObserve,SigNoz,Graylog ,Syslog-ng,Highlight,接下来我会对这几个一一做分析。 1. ELK ELK 是 Elasticsearch、Logsta…...

苹果Find My产品需求增长迅速,伦茨科技ST17H6x芯片供货充足
苹果的Find My功能使得用户可以轻松查找iPhone、Mac、AirPods以及Apple Watch等设备。如今Find My还进入了耳机、充电宝、箱包、电动车、保温杯等多个行业。苹果发布AirTag发布以来,大家都更加注重物品的防丢,苹果的 Find My 就可以查找 iPhone、Mac、Ai…...

题目:忐忑楼梯Ⅱ
问题描述: 解题思路: 利用差分,当第一个以后的差分元素都为零时就代表楼梯高度等于第一个楼梯的高度。为什么是第一个呢,因为以第一个为标准的区间操作数最少。 注意点:每次都只能加一或减一,ans开ll 题解&…...

TS函数类型
函数类型表达式 function hello(x: string) {console.log(x) } //greeter函数的参数是一个函数fn,fn也有一个string类型参数,无返回值。 function greeter(fn: (a: string) > void) {fn(hello) } greeter(hello)也可以把定义参数类型的语句单独提取出…...

数据链路层(四):数据链路层协议
目录 1 数据链路层协议1.1 异步协议1.2 同步协议1.3 局域网数据链路层协议1.4 广域网数据链路层协议 1 数据链路层协议 数据链路层“协议”也称为“规程”,数据链路控制协议也称数据链路控制规程。 数据链路控制协议主要分为异步协议和同步协议两大类。 1.1 异步协…...

#Linux系统编程(孤儿进程及僵尸进程以及wait函数)
(一)发行版:Ubuntu16.04.7 (二)记录: (1)概述 在 Unix/Linux 系统中,正常情况下,子进程是通过父进程创建的,且两者的运行是相互独立的ÿ…...

苍穹外卖项目-01(开发流程,介绍,开发环境搭建,nginx反向代理,Swagger)
目录 一、软件开发整体介绍 1. 软件开发流程 1 第1阶段: 需求分析 2 第2阶段: 设计 3 第3阶段: 编码 4 第4阶段: 测试 5 第5阶段: 上线运维 2. 角色分工 3. 软件环境 1 开发环境(development) 2 测试环境(testing) 3 生产环境(production) 二、苍穹外卖项目介绍 …...

学习笔记(16)函数防抖和节流
JavaScript 中的函数防抖(Debounce)和函数节流(Throttle)是两种优化频繁触发事件回调函数执行的技术,它们主要用于限制函数调用的频率,尤其是在处理高频率触发且响应开销较大的用户交互场景时。 函数防抖 …...

【揭秘】空号检测平台挑选秘籍:让每一分钱都花在“刀刃”上
在数字化营销时代,精准的数据是企业制胜的关键。而空号检测平台作为数据清洗的重要工具,其选择的正确与否直接影响到营销效果与成本效益。如何在众多平台中慧眼识珠,找到最适合自己的“黄金搭档”?今天,就跟着企讯通一…...

Linux源码包安装
目录 一、transmission源码包安装 二、 nginx源码包安装 一、transmission源码包安装 1、下载编译环境所需的软件包依赖 2、下载transmision源码包到用户主目录下 https://github.com/transmission/transmission/releases/download/4.0.5/transmission-4.0.5.tar.xz 3、解压…...

【P1328】[NOIP2014 提高组] 生活大爆炸版石头剪刀布
[NOIP2014 提高组] 生活大爆炸版石头剪刀布 题目背景 NOIP2014 提高组 D1T1 题目描述 石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头。如果两个人出拳一样,则不分胜负。在《生活大爆炸》第二季第 8 集中出现了一种…...

查看k8s中的secret
kubectl get secret xxx -o yaml -n xxxx参考: Managing Secrets using kubectl...

[深度学习]yolov8+pyqt5搭建精美界面GUI设计源码实现五
【简单介绍】 依托先进的目标检测算法YOLOv8与灵活的PyQt5界面开发框架,我们倾力打造出了一款集直观、易用与功能强大于一体的目标检测GUI界面软件。通过深度融合YOLOv8在目标识别领域的出色性能与PyQt5的精美界面设计,我们成功推出了一款高效且稳定的软…...

C# get set 访问器
在C#中get 访问器set 访问器属性的优势例子 在C#中 get 和 set 是访问器(accessors)的关键词,它们用于定义属性的读取和写入行为。属性是一种特殊的类成员,它提供了对字段(field)或计算结果的灵活访问。 …...

【OpenGL】使用 python + Qt + OpenGL 的现代渲染
伴随资源 目录 一、说明二、 PyQt4三、PyOpenGL四、OpenGL 管线五、Python集成开发环境5.1 Emacs配置5.2 pycharm环境 六、你好,OpenGL!七、QGL控件八、平截头体.svg九、定义几何9.1 立即模式与保留模式9.2 使用 VBO 定义 Cube 十、渲染立方体十一、渲…...

vlan、三层交换机、网关、DNS、子网掩码、MAC地址详解
vlan、三层交换机、网关、DNS、子网掩码、MAC地址详解 一、 什么是VLAN? VLAN中文是“虚拟局域网”。 LAN可以是由少数几台家用计算机构成的网络,也可以是数以百计的计算机构成的企业网络。 VLAN所指的LAN特指使用路由器分割的网络——也就是广…...

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记11:数字电位器MCP4017
系列文章目录 嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记01:赛事介绍与硬件平台 嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记02:开发环境安装 嵌入式|蓝桥杯STM32G431(…...

CTF题型 nodejs(1) 命令执行绕过典型例题
CTF题型 nodejs(1) 命令执行绕过 文章目录 CTF题型 nodejs(1) 命令执行绕过一.nodejs中的命令执行二.nodejs中的命令绕过1.编码绕过2.拼接绕过3.模板字符串4.Obejct.keys5.反射6.过滤中括号的情况典型例题1.[GFCTF 2021]ez_calc2.[西湖论剑 2022]Node Magical Login 一.nodejs中…...

Mybatis在SpringBoot中是如何被加载执行
首先依赖于springboot的自动装配EnableAutoConfiguration注解,这个注解最终帮助我们读取mybatis-spring-boot-autoconfigure-x.x.x.jar中的META-INF\spring.factories配置类: org.springframework.boot.autoconfigure.EnableAutoConfiguration\ org.myb…...