请求执行
ExecutionGraphQlService
是调用 GraphQL Java 执行请求的主要 Spring 抽象。底层传输,例如 HTTP,委托给 ExecutionGraphQlService
来处理请求。
主要实现 DefaultExecutionGraphQlService
使用 GraphQlSource
配置,以访问要调用的 graphql.GraphQL
实例。
GraphQLSource
GraphQlSource
是一个契约,用于公开要使用的 graphql.GraphQL
实例,其中还包含一个用于构建该实例的构建器 API。默认构建器可通过 GraphQlSource.schemaResourceBuilder()
获得。
The Boot Starter 创建此构建器的实例,并进一步对其进行初始化,以 从可配置位置加载架构文件,以 公开属性 应用于 GraphQlSource.Builder
,以检测 RuntimeWiringConfigurer
bean,Instrumentation bean 用于 GraphQL 指标,以及 DataFetcherExceptionResolver
和 SubscriptionExceptionResolver
bean 用于 异常解析。 为了进一步定制,您还可以声明一个 GraphQlSourceBuilderCustomizer
bean,例如
@Configuration(proxyBeanMethods = false)
class GraphQlConfig {
@Bean
public GraphQlSourceBuilderCustomizer sourceBuilderCustomizer() {
return (builder) ->
builder.configureGraphQl(graphQlBuilder ->
graphQlBuilder.executionIdProvider(new CustomExecutionIdProvider()));
}
}
架构资源
GraphQlSource.Builder
可以配置一个或多个 Resource
实例,以便对其进行解析并合并在一起。 这意味着架构文件可以从几乎任何位置加载。
默认情况下,Boot 启动器 在位置 classpath:graphql/**
下查找扩展名为 ".graphqls" 或 ".gqls" 的架构文件,通常为 src/main/resources/graphql
。 您也可以使用文件系统位置,或 Spring Resource
层次结构支持的任何位置,包括从远程位置、存储或内存加载架构文件的自定义实现。
使用 classpath*:graphql/**/ 在多个类路径位置(例如,跨多个模块)中查找架构文件。
|
架构创建
默认情况下,GraphQlSource.Builder
使用 GraphQL Java SchemaGenerator
创建 graphql.schema.GraphQLSchema
。 这适用于典型用法,但如果您需要使用不同的生成器,则可以注册 schemaFactory
回调
GraphQlSource.Builder builder = ...
builder.schemaResources(..)
.configureRuntimeWiring(..)
.schemaFactory((typeDefinitionRegistry, runtimeWiring) -> {
// create GraphQLSchema
})
有关如何使用 Spring Boot 配置此内容,请参阅 GraphQlSource 部分。
如果您对联合感兴趣,请参阅 联合 部分。
RuntimeWiringConfigurer
RuntimeWiringConfigurer
用于注册以下内容
-
自定义标量类型。
-
处理 指令 的代码。
-
直接
DataFetcher
注册。 -
等等…
Spring 应用程序通常不需要执行直接的DataFetcher 注册。相反,控制器方法通过AnnotatedControllerConfigurer 注册为DataFetcher ,AnnotatedControllerConfigurer 是一个RuntimeWiringConfigurer 。
|
GraphQL Java,服务器应用程序仅使用 Jackson 对数据映射进行序列化和反序列化。客户端输入被解析为映射。服务器输出根据字段选择集组装成映射。这意味着您不能依赖 Jackson 序列化/反序列化注释。相反,您可以使用自定义标量类型。 |
该启动启动器检测类型为RuntimeWiringConfigurer
的 Bean 并将其注册到GraphQlSource.Builder
中。这意味着在大多数情况下,您的配置中将包含以下内容
@Configuration
public class GraphQlConfig {
@Bean
public RuntimeWiringConfigurer runtimeWiringConfigurer(BookRepository repository) {
GraphQLScalarType scalarType = ... ;
SchemaDirectiveWiring directiveWiring = ... ;
return wiringBuilder -> wiringBuilder
.scalar(scalarType)
.directiveWiring(directiveWiring);
}
}
如果您需要添加WiringFactory
,例如,为了进行考虑架构定义的注册,请实现接受RuntimeWiring.Builder
和输出List<WiringFactory>
的备用configure
方法。这允许您添加任意数量的工厂,然后按顺序调用这些工厂。
TypeResolver
GraphQlSource.Builder
注册ClassNameTypeResolver
作为默认的TypeResolver
,用于 GraphQL 接口和联合,这些接口和联合还没有通过RuntimeWiringConfigurer
进行此类注册。TypeResolver
在 GraphQL Java 中的目的是确定 GraphQL 对象类型,该类型用于从DataFetcher
返回的值,用于 GraphQL 接口或联合字段。
ClassNameTypeResolver
尝试将值的简单类名与 GraphQL 对象类型匹配,如果匹配不成功,它还会遍历其超类型,包括基类和接口,寻找匹配项。ClassNameTypeResolver
提供了一个选项来配置名称提取函数以及Class
到 GraphQL 对象类型名称的映射,这将有助于覆盖更多极端情况
GraphQlSource.Builder builder = ...
ClassNameTypeResolver classNameTypeResolver = new ClassNameTypeResolver();
classNameTypeResolver.setClassNameExtractor((klass) -> {
// Implement Custom ClassName Extractor here
});
builder.defaultTypeResolver(classNameTypeResolver);
有关如何使用 Spring Boot 配置此内容,请参阅 GraphQlSource 部分。
指令
GraphQL 语言支持指令,这些指令“描述 GraphQL 文档中备用运行时执行和类型验证行为”。指令类似于 Java 中的注释,但在 GraphQL 文档中的类型、字段、片段和操作上声明。
GraphQL Java 提供了SchemaDirectiveWiring
契约来帮助应用程序检测和处理指令。有关更多详细信息,请参阅 GraphQL Java 文档中的架构指令。
在 Spring GraphQL 中,您可以通过RuntimeWiringConfigurer
注册SchemaDirectiveWiring
。该启动启动器检测此类 Bean,因此您可能拥有类似以下内容
@Configuration
public class GraphQlConfig {
@Bean
public RuntimeWiringConfigurer runtimeWiringConfigurer() {
return builder -> builder.directiveWiring(new MySchemaDirectiveWiring());
}
}
有关指令支持的示例,请查看 GraphQL Java 的扩展验证 库。 |
ExecutionStrategy
GraphQL Java 中的 ExecutionStrategy
驱动请求字段的获取。要创建 ExecutionStrategy
,您需要提供一个 DataFetcherExceptionHandler
。默认情况下,Spring for GraphQL 会创建异常处理程序,如 异常 中所述,并将其设置在 GraphQL.Builder
上。然后,GraphQL Java 使用它来创建具有配置的异常处理程序的 AsyncExecutionStrategy
实例。
如果您需要创建自定义 ExecutionStrategy
,您可以检测 DataFetcherExceptionResolver
并以相同的方式创建异常处理程序,并使用它来创建自定义 ExecutionStrategy
。例如,在 Spring Boot 应用程序中
@Bean
GraphQlSourceBuilderCustomizer sourceBuilderCustomizer(
ObjectProvider<DataFetcherExceptionResolver> resolvers) {
DataFetcherExceptionHandler exceptionHandler =
DataFetcherExceptionResolver.createExceptionHandler(resolvers.stream().toList());
AsyncExecutionStrategy strategy = new CustomAsyncExecutionStrategy(exceptionHandler);
return sourceBuilder -> sourceBuilder.configureGraphQl(builder ->
builder.queryExecutionStrategy(strategy).mutationExecutionStrategy(strategy));
}
模式转换
如果您想在创建模式后遍历和转换模式,并对模式进行更改,则可以通过 builder.schemaResources(..).typeVisitorsToTransformSchema(..)
注册 graphql.schema.GraphQLTypeVisitor
。请记住,这比 模式遍历 更昂贵,因此通常优先选择遍历而不是转换,除非您需要进行模式更改。
模式遍历
如果您想在创建模式后遍历模式,并可能对 GraphQLCodeRegistry
应用更改,则可以通过 builder.schemaResources(..).typeVisitors(..)
注册 graphql.schema.GraphQLTypeVisitor
。但是,请记住,这样的访问者无法更改模式。如果您需要对模式进行更改,请参阅 模式转换。
模式映射检查
如果查询、变异或订阅操作没有 DataFetcher
,它将不会返回任何数据,也不会执行任何有用的操作。同样,模式类型的字段,既没有通过 DataFetcher
注册显式覆盖,也没有通过找到匹配 Class
属性的默认 PropertyDataFetcher
隐式覆盖,将始终为 null
。
GraphQL Java 不会执行检查以确保每个模式字段都被覆盖,并且作为更底层的库,GraphQL Java 只是不知道 DataFetcher
可以返回什么或它依赖于哪些参数,因此无法执行此类验证。这会导致差距,具体取决于测试覆盖率,可能直到运行时才会被发现,当客户端可能遇到“静默”null
值或非空字段错误时。
Spring 中用于 GraphQL 的 SelfDescribingDataFetcher
接口允许 DataFetcher
公开信息,例如返回类型和预期参数。所有内置的 Spring DataFetcher
实现,包括用于 控制器方法、用于 Querydsl 和用于 Query by Example 的实现,都是此接口的实现。对于带注释的控制器,返回类型和预期参数基于控制器方法签名。这使得在启动时检查模式映射成为可能,以确保以下内容
-
模式字段要么具有
DataFetcher
注册,要么具有相应的Class
属性。 -
DataFetcher
注册引用存在的模式字段。 -
DataFetcher
参数具有匹配的模式字段参数。
要启用模式检查,请自定义 GraphQlSource.Builder
,如下所示。在这种情况下,报告只是被记录,但您可以选择采取任何操作
GraphQlSource.Builder builder = ...
builder.schemaResources(..)
.inspectSchemaMappings(report -> {
logger.debug(report);
});
示例报告
GraphQL schema inspection: Unmapped fields: {Book=[title], Author[firstName, lastName]} (1) Unmapped registrations: {Book.reviews=BookController#reviews[1 args]} (2) Unmapped arguments: {BookController#bookSearch[1 args]=[myAuthor]} (3) Skipped types: [BookOrAuthor] (4)
1 | 未涵盖的模式字段 |
2 | DataFetcher 注册到不存在的字段 |
3 | 不存在的 DataFetcher 预期参数 |
4 | 已跳过的模式类型(将在下一节中解释) |
在某些情况下,模式类型的 Class
类型未知。也许 DataFetcher
未实现 SelfDescribingDataFetcher
,或者声明的返回类型过于通用(例如 Object
)或未知(例如 List<?>
),或者可能完全缺少 DataFetcher
。在这种情况下,模式类型被列为已跳过,因为它无法被验证。对于每个跳过的类型,都会显示一条 DEBUG 消息解释跳过原因。
联合和接口
对于联合,检查会遍历成员类型并尝试查找相应的类。对于接口,检查会遍历实现类型并查找相应的类。
默认情况下,可以在以下情况下开箱即用地检测到相应的 Java 类
-
Class
的简单名称与 GraphQL 联合成员或接口实现类型名称匹配,并且Class
位于与映射到联合或接口字段的控制器方法或控制器类的返回类型相同的包中。 -
在模式的其他部分,其中映射的字段是具体联合成员或接口实现类型,会检查
Class
。 -
您已注册了具有显式
Class
到 GraphQL 类型映射的 TypeResolver。
如果以上方法均无效,并且 GraphQL 类型在模式检查报告中被报告为已跳过,您可以进行以下自定义操作
-
显式地将 GraphQL 类型名称映射到 Java 类或类。
-
配置一个函数,该函数自定义如何将 GraphQL 类型名称调整为简单的
Class
名称。这有助于处理特定的 Java 类命名约定。 -
提供一个
ClassNameTypeResolver
来将 GraphQL 类型映射到 Java 类。
例如
GraphQlSource.Builder builder = ...
builder.schemaResources(..)
.inspectSchemaMappings(
initializer -> initializer.classMapping("Author", Author.class)
logger::debug);
操作缓存
GraphQL Java 必须在执行操作之前解析和验证它。这可能会严重影响性能。为了避免重新解析和验证的需要,应用程序可以配置一个 PreparsedDocumentProvider
来缓存和重用 Document 实例。有关通过 PreparsedDocumentProvider
进行查询缓存的更多详细信息,请参阅 GraphQL Java 文档。
在 Spring GraphQL 中,您可以通过 GraphQlSource.Builder#configureGraphQl
注册 PreparsedDocumentProvider
:.
// Typically, accessed through Spring Boot's GraphQlSourceBuilderCustomizer
GraphQlSource.Builder builder = ...
// Create provider
PreparsedDocumentProvider provider =
new ApolloPersistedQuerySupport(new InMemoryPersistedQueryCache(Collections.emptyMap()));
builder.schemaResources(..)
.configureRuntimeWiring(..)
.configureGraphQl(graphQLBuilder -> graphQLBuilder.preparsedDocumentProvider(provider))
有关如何使用 Spring Boot 配置此内容,请参阅 GraphQlSource 部分。
线程模型
大多数 GraphQL 请求从获取嵌套字段的并发执行中受益。这就是为什么大多数应用程序如今依赖于 GraphQL Java 的 AsyncExecutionStrategy
,它允许数据提取器返回 CompletionStage
并并发执行而不是串行执行。
Java 21 和虚拟线程增加了有效使用更多线程的重要能力,但仍然需要并发执行而不是串行执行,以便请求执行更快地完成。
Spring for GraphQL 支持
-
响应式数据提取器,并且这些提取器将根据
AsyncExecutionStrategy
的预期适应CompletionStage
。 -
CompletionStage
作为返回值。 -
作为 Kotlin 协程方法的控制器方法。
-
@SchemaMapping 和 @BatchMapping 方法可以返回
Callable
,该Callable
会提交给Executor
(例如 Spring FrameworkVirtualThreadTaskExecutor
)。要启用此功能,您必须在AnnotatedControllerConfigurer
上配置一个Executor
。
Spring for GraphQL 在 Spring MVC 或 WebFlux 上运行作为传输。Spring MVC 使用异步请求执行,除非生成的 CompletableFuture
在 GraphQL Java 引擎返回后立即完成,这将是请求足够简单且不需要异步数据获取的情况。
响应式 DataFetcher
默认的 GraphQlSource
构建器支持 DataFetcher
返回 Mono
或 Flux
,并将它们适配为 CompletableFuture
,其中 Flux
值被聚合并转换为列表,除非请求是 GraphQL 订阅请求,在这种情况下,返回值将保持为 Reactive Streams Publisher
以用于流式传输 GraphQL 响应。
一个响应式的 DataFetcher
可以依赖于从传输层传播的 Reactor 上下文,例如来自 WebFlux 请求处理,请参阅 WebFlux 上下文。
上下文传播
Spring for GraphQL 提供支持,以透明地将上下文从 HTTP 传输层,通过 GraphQL Java,传播到 DataFetcher
和它调用的其他组件。这包括来自 Spring MVC 请求处理线程的 ThreadLocal
上下文和来自 WebFlux 处理管道的 Reactor Context
。
WebMvc
DataFetcher
和 GraphQL Java 调用的其他组件可能并不总是在与 Spring MVC 处理程序相同的线程上执行,例如,如果一个异步的 WebGraphQlInterceptor
或 DataFetcher
切换到另一个线程。
Spring for GraphQL 支持将 ThreadLocal
值从 Servlet 容器线程传播到 DataFetcher
和 GraphQL Java 调用的其他组件执行的线程。为此,应用程序需要为感兴趣的 ThreadLocal
值实现 io.micrometer.context.ThreadLocalAccessor
。
public class RequestAttributesAccessor implements ThreadLocalAccessor<RequestAttributes> {
@Override
public Object key() {
return RequestAttributesAccessor.class.getName();
}
@Override
public RequestAttributes getValue() {
return RequestContextHolder.getRequestAttributes();
}
@Override
public void setValue(RequestAttributes attributes) {
RequestContextHolder.setRequestAttributes(attributes);
}
@Override
public void reset() {
RequestContextHolder.resetRequestAttributes();
}
}
您可以在启动时使用全局 ContextRegistry
实例手动注册 ThreadLocalAccessor
,该实例可以通过 io.micrometer.context.ContextRegistry#getInstance()
访问。您也可以通过 java.util.ServiceLoader
机制自动注册它。
WebFlux
一个 响应式的 DataFetcher
可以依赖于来自 WebFlux 请求处理链的 Reactor 上下文。这包括由 WebGraphQlInterceptor 组件添加的 Reactor 上下文。
异常
在 GraphQL Java 中,DataFetcherExceptionHandler
决定如何将数据获取中的异常表示为响应的“错误”部分。应用程序只能注册一个处理程序。
Spring for GraphQL 注册一个 DataFetcherExceptionHandler
,它提供默认处理并启用 DataFetcherExceptionResolver
合同。应用程序可以通过 GraphQLSource
构建器注册任意数量的解析器,并且这些解析器按顺序执行,直到其中一个解析器将 Exception
解析为 List<graphql.GraphQLError>
。Spring Boot 启动器会检测此类型的 Bean。
DataFetcherExceptionResolverAdapter
是一个方便的基类,它具有受保护的方法 resolveToSingleError
和 resolveToMultipleErrors
。
使用 带注解的控制器 编程模型,可以使用带注解的异常处理程序方法来处理数据获取异常,这些方法具有灵活的方法签名,请参阅 @GraphQlExceptionHandler
获取详细信息。
GraphQLError
可以根据 GraphQL Java 的 graphql.ErrorClassification
或 Spring GraphQL 的 ErrorType
分配到一个类别,ErrorType
定义了以下内容
-
BAD_REQUEST
-
UNAUTHORIZED
-
FORBIDDEN
-
NOT_FOUND
-
INTERNAL_ERROR
如果异常未得到解决,默认情况下,它将被归类为 INTERNAL_ERROR
,并附带一个包含类别名称和 DataFetchingEnvironment
中的 executionId
的通用消息。该消息故意不透明,以避免泄露实现细节。应用程序可以使用 DataFetcherExceptionResolver
自定义错误详细信息。
未解决的异常将记录在 ERROR 级别,并附带 executionId
以与发送给客户端的错误相关联。已解决的异常将记录在 DEBUG 级别。
请求异常
GraphQL Java 引擎在解析请求时可能会遇到验证或其他错误,从而阻止请求执行。在这种情况下,响应将包含一个带有 null
的“data”键和一个或多个请求级别的“errors”,这些错误是全局的,即没有字段路径。
DataFetcherExceptionResolver
无法处理此类全局错误,因为它们是在执行开始之前和调用任何 DataFetcher
之前引发的。应用程序可以使用传输级别拦截器来检查和转换 ExecutionResult
中的错误。有关示例,请参阅 WebGraphQlInterceptor
下的内容。
订阅异常
订阅请求的 Publisher
可能会完成一个错误信号,在这种情况下,底层传输(例如 WebSocket)将发送一个带有 GraphQL 错误列表的最终“error”类型消息。
DataFetcherExceptionResolver
无法解决订阅 Publisher
中的错误,因为数据 DataFetcher
仅在最初创建 Publisher
。之后,传输将订阅 Publisher
,然后 Publisher
可能会完成一个错误。
应用程序可以注册一个 SubscriptionExceptionResolver
,以便解决订阅 Publisher
中的异常,以便将这些异常解析为发送给客户端的 GraphQL 错误。
分页
GraphQL 游标连接规范 定义了一种通过一次返回结果集的子集来导航大型结果集的方法,其中每个项目都与一个游标配对,客户端可以使用该游标来请求引用的项目之前或之后的更多项目。
规范将该模式称为“Connections”。名称以 Connection 结尾的模式类型是Connection Type,它表示一个分页结果集。所有~Connection
类型都包含一个“edges”字段,其中~Edge
类型将实际项目与游标配对,以及一个“pageInfo”字段,其中包含布尔标志,用于指示是否有更多项目向前和向后。
Connection 类型
必须为每个需要分页的类型创建Connection
类型定义,这会向模式添加样板代码和噪音。Spring for GraphQL 提供了ConnectionTypeDefinitionConfigurer
,用于在启动时添加这些类型,如果解析的模式文件中不存在这些类型。这意味着在模式中你只需要以下内容
Query {
books(first:Int, after:String, last:Int, before:String): BookConnection
}
type Book {
id: ID!
title: String!
}
请注意规范定义的向前分页参数first
和after
,客户端可以使用这些参数来请求给定游标后的前 N 个项目,而last
和before
是向后分页参数,用于请求给定游标前的最后 N 个项目。
接下来,配置ConnectionTypeDefinitionConfigurer
如下
GraphQlSource.schemaResourceBuilder()
.schemaResources(..)
.typeDefinitionConfigurer(new ConnectionTypeDefinitionConfigurer)
以下类型定义将透明地添加到模式中
type BookConnection {
edges: [BookEdge]!
pageInfo: PageInfo!
}
type BookEdge {
node: Book!
cursor: String!
}
type PageInfo {
hasPreviousPage: Boolean!
hasNextPage: Boolean!
startCursor: String
endCursor: String
}
Boot Starter 默认情况下注册ConnectionTypeDefinitionConfigurer
。
ConnectionAdapter
一旦Connection 类型 在模式中可用,你也需要等效的 Java 类型。GraphQL Java 提供了这些类型,包括泛型Connection
和Edge
,以及PageInfo
。
一种选择是填充一个Connection
并从你的控制器方法或DataFetcher
中返回它。但是,这需要样板代码来创建Connection
,创建游标,将每个项目包装为Edge
,以及创建PageInfo
。此外,你可能已经拥有一个底层的分页机制,例如使用 Spring Data 存储库时。
Spring for GraphQL 定义了ConnectionAdapter
合同,用于将项目容器适配到Connection
。适配器通过DataFetcher
装饰器应用,该装饰器又通过ConnectionFieldTypeVisitor
安装。你可以按如下方式配置它
ConnectionAdapter adapter = ... ;
GraphQLTypeVisitor visitor = ConnectionFieldTypeVisitor.create(List.of(adapter)) (1)
GraphQlSource.schemaResourceBuilder()
.schemaResources(..)
.typeDefinitionConfigurer(..)
.typeVisitors(List.of(visitor)) (2)
1 | 使用一个或多个ConnectionAdapter 创建类型访问器。 |
2 | 注册类型访问器。 |
有内置 的ConnectionAdapter
用于 Spring Data 的Window
和Slice
。你也可以创建自己的自定义适配器。ConnectionAdapter
实现依赖于CursorStrategy
来为返回的项目创建游标。相同的策略也用于支持Subrange
控制器方法参数,该参数包含分页输入。
CursorStrategy
CursorStrategy
是一个用于对字符串游标进行编码和解码的契约,该游标引用大型结果集中项目的所在位置。游标可以基于索引或键集。
ConnectionAdapter
使用它来对返回项目的游标进行编码。 带注释的控制器 方法、Querydsl 存储库和 Query by Example 存储库使用它来从分页请求中解码游标,并创建一个 Subrange
。
CursorEncoder
是一个相关的契约,它进一步对字符串游标进行编码和解码,使其对客户端不透明。EncodingCursorStrategy
将 CursorStrategy
与 CursorEncoder
相结合。您可以使用 Base64CursorEncoder
、NoOpEncoder
或创建您自己的编码器。
有一个 内置的 CursorStrategy
用于 Spring Data ScrollPosition
。当 Spring Data 存在时,Boot Starter 会使用 Base64Encoder
注册一个 CursorStrategy<ScrollPosition>
。
排序
在 GraphQL 请求中没有标准的方法来提供排序信息。但是,分页依赖于稳定的排序顺序。您可以使用默认顺序,或者公开输入类型并从 GraphQL 参数中提取排序详细信息。
有一个 内置的 支持用于 Spring Data 的 Sort
作为控制器方法参数。要使此方法起作用,您需要有一个 SortStrategy
bean。
批量加载
给定一个 Book
及其 Author
,我们可以为一本书创建一个 DataFetcher
,为其作者创建另一个 DataFetcher
。这允许选择有或没有作者的书籍,但这意味着书籍和作者不会一起加载,这在查询多本书时效率特别低,因为每本书的作者都是单独加载的。这被称为 N+1 选择问题。
DataLoader
GraphQL Java 提供了一个 DataLoader
机制,用于对相关实体进行批量加载。您可以在 GraphQL Java 文档 中找到完整详细信息。以下是其工作原理的摘要
-
在
DataLoaderRegistry
中注册DataLoader
,它可以根据唯一键加载实体。 -
DataFetcher
可以访问DataLoader
并使用它们通过 ID 加载实体。 -
DataLoader
通过返回一个 future 来延迟加载,以便它可以批量完成。 -
DataLoader
会维护一个针对每个请求的已加载实体缓存,这可以进一步提高效率。
BatchLoaderRegistry
GraphQL Java 中完整的批量加载机制需要实现几个 BatchLoader
接口之一,然后将它们包装并注册为 DataLoader
,并使用 DataLoaderRegistry
中的名称进行注册。
Spring GraphQL 中的 API 略有不同。对于注册,只有一个中央 BatchLoaderRegistry
,它公开工厂方法和构建器,用于创建和注册任意数量的批量加载函数。
@Configuration
public class MyConfig {
public MyConfig(BatchLoaderRegistry registry) {
registry.forTypePair(Long.class, Author.class).registerMappedBatchLoader((authorIds, env) -> {
// return Mono<Map<Long, Author>
});
// more registrations ...
}
}
该 Boot Starter 声明了一个 BatchLoaderRegistry
bean,您可以将其注入到您的配置中,如上所示,或者注入到任何组件(例如控制器)中,以便注册批量加载函数。反过来,BatchLoaderRegistry
会被注入到 DefaultExecutionGraphQlService
中,它确保每个请求的 DataLoader
注册。
默认情况下,DataLoader
名称基于目标实体的类名。这允许 @SchemaMapping
方法声明一个 DataLoader 参数,使用泛型类型,而无需指定名称。但是,如果需要,可以通过 BatchLoaderRegistry
构建器自定义名称,以及其他 DataLoaderOptions
。
要全局配置默认 DataLoaderOptions
,将其用作任何注册的起点,您可以覆盖 Boot 的 BatchLoaderRegistry
bean,并使用接受 Supplier<DataLoaderOptions>
的 DefaultBatchLoaderRegistry
构造函数。
在许多情况下,当加载相关实体时,您可以使用 @BatchMapping 控制器方法,它们是使用 BatchLoaderRegistry
和 DataLoader
的快捷方式,并取代了直接使用它们的需求。
BatchLoaderRegistry
还提供了其他重要优势。它支持从批量加载函数和 @BatchMapping
方法访问相同的 GraphQLContext
,并确保 上下文传播 到它们。这就是应用程序预期使用它的原因。可以执行您自己的 DataLoader
注册,但此类注册将放弃上述优势。
测试批量加载
首先让 BatchLoaderRegistry
在 DataLoaderRegistry
上执行注册。
BatchLoaderRegistry batchLoaderRegistry = new DefaultBatchLoaderRegistry();
// perform registrations...
DataLoaderRegistry dataLoaderRegistry = DataLoaderRegistry.newRegistry().build();
batchLoaderRegistry.registerDataLoaders(dataLoaderRegistry, graphQLContext);
现在您可以访问和测试各个 DataLoader
,如下所示。
DataLoader<Long, Book> loader = dataLoaderRegistry.getDataLoader(Book.class.getName());
loader.load(1L);
loader.loadMany(Arrays.asList(2L, 3L));
List<Book> books = loader.dispatchAndJoin(); // actual loading
assertThat(books).hasSize(3);
assertThat(books.get(0).getName()).isEqualTo("...");
// ...