预先优化
本章介绍了 Spring Data 的提前(AOT)优化,这些优化基于 Spring 的提前优化。
运行时提示
将应用程序作为原生镜像运行需要比常规 JVM 运行时更多的信息。Spring Data 在 AOT 处理期间为原生镜像使用贡献了 运行时提示。这些提示特别适用于
-
审计
-
ManagedTypes用于捕获类路径扫描的结果 -
Repositories
-
实体、返回类型和 Spring Data 注解的反射提示
-
Repository 片段
-
Querydsl
Q类 -
Kotlin 协程支持
-
-
Web 支持(
PagedModel的 Jackson 提示)
提前(AOT)Repository
AOT Repository 是 AOT 处理的扩展,通过预生成符合条件的查询方法实现。查询方法对开发人员来说是不透明的,因为它隐藏了在查询方法调用中执行的底层查询。AOT Repository 基于在构建时已知的派生、注解和命名查询来贡献查询方法实现。这种优化将查询方法处理从运行时移至构建时,这可以显著提高性能,因为查询方法无需在每次应用程序启动时进行反射分析。
生成的 AOT 仓库片段遵循 <Repository FQCN>Impl__Aot 的命名方案,并放置在与仓库接口相同的包中。您可以在生成的仓库查询方法中找到所有以字符串形式存在的查询。
| 请将 AOT Repository 类视为内部优化。不要在您的代码中直接使用它们,因为生成和实现细节可能会在未来的版本中发生变化。 |
使用 AOT 仓库运行
AOT 是将 Spring 应用程序转换为原生可执行文件的一个强制步骤,因此在此模式下运行时会自动启用。当启用 AOT(无论是用于原生编译还是通过设置 spring.aot.enabled=true)时,也会生成 AOT 仓库。
您可以完全禁用 AOT 仓库生成,或者只禁用 JDBC AOT 仓库。
-
设置
spring.aot.repositories.enabled=false属性可以禁用所有 Spring Data 模块的生成仓库。 -
设置
spring.aot.jdbc.repositories.enabled=false属性可以仅禁用 JDBC AOT 仓库。
AOT 仓库会向实际的仓库 bean 注册贡献配置更改,以注册生成的仓库片段。
| 当包含 AOT 优化时,一些在构建时做出的决定会被硬编码到应用程序设置中。例如,在构建时启用的配置文件在运行时也会自动启用。此外,实现仓库的 Spring Data 模块是固定的。更改实现需要重新进行 AOT 处理。 |
请提供 JdbcDialect 以避免方言检测导致的早期数据库访问。 |
符合条件的方法
AOT 仓库会过滤符合 AOT 处理条件的方法。这些方法通常是不由实现片段支持的所有查询方法。
支持的功能
-
派生查询方法、
@Query和命名查询方法 -
返回
void、int和long的@Modifying方法 -
分页、
Slice、Stream和Optional返回类型 -
DTO 和接口投影
-
值表达式
限制
-
接受
ScrollPosition(例如Keyset分页)的方法尚不支持
排除的方法
-
CrudRepository、Querydsl、Query by Example 以及其他基本接口方法,因为它们的实现由基类各自的片段提供 -
实现过于复杂的方法
Repository 元数据
AOT 处理会内省查询方法并收集有关仓库查询的元数据。Spring Data JDBC 将此元数据存储在 JSON 文件中,这些文件以仓库接口命名并与其存储在同一位置(即同一包中)。仓库 JSON 元数据包含有关查询和片段的详细信息。以下是以下仓库的示例:
interface UserRepository extends CrudRepository<User, Integer> {
List<User> findUserNoArgumentsBy(); (1)
Page<User> findPageOfUsersByLastnameStartingWith(String lastname, Pageable page); (2)
@Query("select * from User u where username = ?1")
User findAnnotatedQueryByEmailAddress(String username); (3)
User findByEmailAddress(String emailAddress); (4)
}
| 1 | 无参数的派生查询。 |
| 2 | 使用分页的派生查询。 |
| 3 | 带注解的查询。 |
| 4 | 命名查询。尽管存储过程方法包含在 JSON 元数据中,但它们的方法代码块不会在 AOT 仓库中生成。 |
{
"name": "com.acme.UserRepository",
"module": "JDBC",
"type": "IMPERATIVE",
"methods": [
{
"name": "findUserNoArgumentsBy",
"signature": "public abstract java.util.List<com.acme.User> com.acme.UserRepository.findUserNoArgumentsBy()",
"query": {
"query": "SELECT * FROM User"
}
},
{
"name": "findPageOfUsersByLastnameStartingWith",
"signature": "public abstract org.springframework.data.domain.Page<com.acme.User> com.acme.UserRepository.findPageOfUsersByLastnameStartingWith(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": "findAnnotatedQueryByEmailAddress",
"signature": "public abstract com.acme.User com.acme.UserRepository.findAnnotatedQueryByEmailAddress(java.lang.String)",
"query": {
"query": "select * from User where emailAddress = ?1"
}
},
{
"name": "findByEmailAddress",
"signature": "public abstract com.acme.User com.acme.UserRepository.findByEmailAddress(java.lang.String)",
"query": {
"name": "User.findByEmailAddress",
"query": "SELECT * FROM User WHERE emailAddress = ?1"
}
},
{
"name": "count",
"signature": "public abstract long org.springframework.data.repository.CrudRepository.count()",
"fragment": {
"fragment": "org.springframework.data.jdbc.repository.support.SimpleJdbcRepository"
}
}
]
}
查询可能包含以下字段:
-
query:如果方法是查询方法,则为查询描述符。-
name:如果查询是命名查询,则为命名查询的名称。 -
query:用于从EntityManager获取查询方法结果的查询 -
count-name:如果计数查询是命名查询,则为命名计数查询的名称。 -
count-query:用于获取使用分页的查询方法的计数的计数查询。
-
-
fragment:如果方法调用委托给存储(仓库基类、功能片段如 Querydsl)或用户片段,则为目标片段。如果不存在进一步的接口,片段仅用fragment描述;如果存在接口(如 Querydsl 或用户声明的片段接口),则以interface和fragment元组形式描述。
|
规范化查询形式
对查询进行静态分析只能有限地表示运行时查询行为。查询以其规范化(预解析和重写)形式表示
|