Spring Data 提前编译存储库 - 第二部分
在迈向正式发布的博客系列收官之际,让我们来探索 Spring Data AOT(提前编译)存储库的优势。
早在 2025 年 5 月,我们首次在下一代 Spring Data 的第三个里程碑版本中,为 JPA 和 MongoDB 引入了 AOT 存储库作为预览功能。
简而言之,此功能利用 AOT 处理,借助存储库特定于数据存储的特性,使用实际的源代码来实现您的存储库查询方法。
此后,我们吸收了反馈,优化了细节,并新增了两个模块的支持:Apache Cassandra 和 JDBC。
这意味着,随着 2025.1.0 版本的发布,您将能够在四个 Spring Data 模块中使用 AOT 生成的存储库:
- Spring Data for Apache Cassandra
- Spring Data JDBC
- Spring Data JPA
- Spring Data MongoDB
您将获得什么
通过利用通用的 Spring AOT 基础设施,我们对您的应用程序配置有了深入的了解,这使得集成比使用注解处理器成为可能。用于 AOT 处理的预初始化应用程序上下文配置使我们能够访问所有已注册的 bean、它们的类型和属性,从而捕获您的配置和意图。
您可以想象,当您需要处理像 Data JDBC 这样的集成时,这种洞察力至关重要:虽然针对的是标准 SQL,但 JDBC 模块需要了解目标数据库可能存在的、常常伴随着自身方言差异的特性。能够访问已注册的 bean,例如 JdbcDialect,使得生成正确的 SQL 语句成为可能。
因此,当我们查看生成的代码时,它在遵循各个模块共同原则的同时,也尽可能贴近底层技术。
正如每个 Spring Data 模块都特定于其目标技术一样,生成的代码也是如此。
一个简单的查询方法,例如 JDBC 中的 findByLastnameStartingWith,其生成代码与为 JPA 或 Apache Cassandra 生成的代码截然不同:
Spring Data JDBC
|
|
Spring Data JPA
|
|
Spring Data for Apache Cassandra
|
|
当构建时启用了通用的 Spring AOT 支持时,AOT 存储库默认会被生成和挂载。
如果您希望,可以通过 spring.aot.repositories.enabled=false 选择不生成存储库代码,或者通过 spring.aot.jdbc.repositories.enabled=false 来选择禁用单个模块。
要实际使用它们,您的应用程序必须在 AOT 模式下启动(通过使用 spring.aot.enabled 属性或作为 GraalVM 原生镜像)。
但是,让我们也看看它在开发和调试方面的一些优势。
可调试性与元数据
AOT 生成的存储库不仅用代码解释了您的查询是如何执行的,您还可以在 IDE 中设置断点以在需要时调试语句。
遵循在 AOT 处理期间了解查询样式的思路,我们为您的 Spring Data 接口引入了一种 JSON 元数据表示形式。
以上述存储库为例,我们暂且称之为 UserRepository,Spring Data 会生成一个 UserRepository.json 资源文件,该文件与您的存储库接口位于同一个包中。
该特定文件包含有关查询方法的信息:它们的目标实现是什么,以及它们将运行什么查询。
它可用于文档目的,但也可以被您喜爱的 IDE 读取,以便在您开发时显示额外信息。
Spring Data JDBC
|
|
Spring Data JPA
|
|
Spring Data for Apache Cassandra
|
|
您可以通过设置 spring.aot.repositories.metadata.enabled=false 来选择性禁用元数据生成。
AOT 缓存与 Project Leyden
预先生成存储库代码不仅具有可调试性优势,而且与最新 Java 版本的最新发展非常契合。
与那些在运行时生成以让程序工作的代码片段不同,预生成的存储库可以在 AOT 缓存训练运行期间被 JVM 看到和分析。
这有助于您通过从 Java 共享对象文件中提供已完全加载和链接的存储库实现来减少启动时间。
|
|
特别是 Spring Data NoSQL 实现的一个众所周知的功能是它们的对象映射能力。
不那么广为人知的是,在运行时,Spring Data 会尝试通过生成使用 MethodHandle 的字节码来优化新实例的创建和域对象属性的访问,从而提高加载/保存性能。
收集有关您的域模型的信息为在构建时包含生成的字节码铺平了道路,这些字节码用于属性访问器和实体实例化器。
已有生成的字节码可用,则无需使用运行时优化。此外,捕获的实体实例化器和属性访问器被打包到捆绑的 JAR 中,因此可以通过类加载器直接获得,从而更高效地提供服务。
|
|
有什么缺点吗?
显然,计算时间并不会凭空消失,而是转移到了其他地方。
在本例中,转移到了构建阶段。因此,是的,分析应用程序、准备数据以及进行 JVM 训练运行都需要时间,并且可能需要对您的构建和部署管道进行更改。
您也在用框架的某些动态特性来换取更少的内存消耗和更快的启动时间。
当选择启用 AOT 时,您的部分配置需要"冻结"。还记得之前的 JdbcDialect 吗?这正是那些冻结点之一,因为您无法生成针对一个数据库优化的 SQL,然后切换到另一个数据库就像什么都没发生一样。
此外,AOT 存储库目前仅支持命令式存储库接口。响应式接口不会触发任何代码生成。
如果您感到好奇,可以直接使用 spring.aot.enabled 构建并运行您的应用程序,或者前往我们的 Spring Data 示例库,那里我们已经为您准备了演示应用程序供您尝试。
无论如何,您始终可以回退到不使用 spring.aot.enabled 标志启动您的应用程序,这将禁用所有 AOT 增强功能。
这使您能够在 AOT 模式下构建和测试应用程序,同时内置了回退策略。
我们很期待看到您如何利用 AOT 特性。
并且,一如既往,您的反馈很重要!
因此,我们期待听到您的声音以及您为改进提供经验的方法。