AOT(提前编译)优化
本章介绍 Spring Data 的 AOT(提前编译)优化,这些优化构建于 Spring 的提前编译优化 之上。
最佳实践
为您的领域类型添加注解
在应用程序启动期间,Spring 会扫描 classpath 查找领域类,以便对实体进行早期处理。通过使用特定于 Spring Data 存储的 @Table、@Document 或 @Entity 注解为您的领域类型添加注解,您可以辅助初始实体扫描,并确保这些类型为运行时提示(Runtime Hints)注册到 ManagedTypes。在原生映像环境中无法进行 classpath 扫描,因此 Spring 必须使用 ManagedTypes 来确定初始实体集。
AOT 代码生成
AOT 代码生成并不仅限于与 GraalVM Native Image 配合使用,在常规部署中也能带来益处,并有助于优化 JVM 上的启动性能。
借助 AOT 优化,某些决策(例如数据库方言)将在构建期被固化,并按原样纳入应用程序的配置中。
如果启用了 AOT 编译,Spring Data 可以(取决于实际使用的模块)在构建的 AOT 阶段贡献多个组件。
-
为生成的类型/属性访问器生成字节码
-
为已定义的仓库接口生成源代码
-
JSON 格式的仓库元数据
上述各项默认均已启用。不过,您可以通过以下选项对配置进行微调。
spring.aot.data.accessors.enabled | 用于控制是否为生成的类型/属性访问器贡献字节码的布尔标志 |
|---|---|
spring.aot.data.accessors.include | 以逗号分隔的 FQCN 列表,为其中列出的类贡献生成的类型/属性访问器字节码。支持以 Ant 风格的包含模式匹配包名(例如 example.springdata.**)或类型名。如果某个类型同时被包含和排除规则匹配,则包含规则优先,该类型视为已包含。 |
spring.aot.data.accessors.exclude | 以逗号分隔的 FQCN 列表,跳过为其中列出的类贡献生成的类型/属性访问器字节码。支持以 Ant 风格的排除模式匹配包名(例如 example.springdata.**)或类型名。如果某个类型同时被包含和排除规则匹配,则包含规则优先,该类型视为已包含。 |
spring.aot.repositories.enabled | 用于控制是否为仓库接口贡献源代码的布尔标志 |
spring.aot.[module-name].repositories.enabled | 用于控制是否为特定模块(例如 cassandra、jdbc、jpa、mongodb)的仓库接口贡献源代码的布尔标志 |
spring.aot.repositories.metadata.enabled | 用于控制是否生成包含查询方法及实际查询字符串的 JSON 仓库元数据的布尔标志。需要先启用 spring.aot.repositories.enabled。 |
AOT 仓库
AOT 仓库仅适用于特定模块的命令式(非响应式)仓库接口。判定合格查询方法的标准因实现模块而异。
AOT 仓库通过预生成合格的查询方法实现,对 AOT 处理进行扩展。对于开发者而言,查询方法在一次调用中底层执行的查询是不透明的。AOT 仓库基于构建期已知的派生查询、注解查询和命名查询贡献查询方法实现。这项优化将查询方法的处理从运行时转移到构建期,从而显著提升性能,因为查询方法无需在每次应用程序启动时通过反射进行分析。
生成的 AOT 仓库片段遵循 <Repository FQCN>Impl__AotRepository 的命名规则,并放置在与仓库接口相同的包中。
请将 AOT 仓库类视为内部优化。请勿在您的代码中直接使用它们,因为其生成与实现细节在未来的版本中可能会发生变化。
仓库元数据
AOT 处理会内省查询方法,并收集有关仓库查询的元数据。Spring Data 将这些元数据存储在 JSON 文件中,文件以源仓库命名,并位于相同的包中。仓库 JSON 元数据包含有关查询和片段的详细信息。以下列仓库为例,其示例如下:
- Metadata
- Repository
{
"name": "example.springdata.UserRepository",
"module": "JDBC",
"type": "IMPERATIVE",
"methods": [
{
"name": "findBy",
"signature": "public abstract java.util.List<example.springdata.User> example.springdata.UserRepository.findBy()",
"query": {
"query": "SELECT * FROM User"
}
},
{
"name": "findByLastnameStartingWith",
"signature": "public abstract org.springframework.data.domain.Page<example.springdata.User> example.springdata.UserRepository.findByLastnameStartingWith(java.lang.String,org.springframework.data.domain.Pageable)",
"query": {
"query": "SELECT * FROM User u WHERE lastname LIKE :lastname",
"count-query": "SELECT COUNT(*) FROM User WHERE lastname LIKE :lastname"
}
},
{
"name": "findByEmailAddress",
"signature": "public abstract example.springdata.User example.springdata.UserRepository.findByEmailAddress(java.lang.String)",
"query": {
"query": "select * from User where emailAddress = ?1"
}
},
interface UserRepository extends CrudRepository<User, Integer> {
List<User> findBy();
Page<User> findByLastnameStartingWith(String lastname, Pageable page);
@Query("select * from User where emailAddress = ?1")
User findByEmailAddress(String username);
}
是否创建 JSON 元数据可通过 spring.aot.repositories.metadata.enabled 标志进行控制。
原生映像运行时提示
与常规 JVM 运行时相比,以原生映像方式运行应用程序需要额外的信息。Spring Data 会在 AOT 处理期间为原生映像使用贡献运行时提示。这些提示尤其针对:
-
审计
-
ManagedTypes,用于捕获 classpath 扫描的结果 -
仓库
-
针对实体、返回类型和 Spring Data 注解的反射提示
-
仓库片段
-
Querydsl
Q类 -
Kotlin 协程支持
-
-
Web 支持(针对
PagedModel的 Jackson 提示)