|
对于最新的稳定版本,请使用Spring GraphQL 2.0.2! |
请求执行
ExecutionGraphQlService 是调用 GraphQL Java 执行请求的主要 Spring 抽象。底层传输,例如 HTTP,将请求委托给 ExecutionGraphQlService 处理。
The main implementation, DefaultExecutionGraphQlService, is configured with a
GraphQlSource for access to the graphql.GraphQL instance to invoke.
GraphQLSource
GraphQlSource 是一个契约,用于暴露 graphql.GraphQL 实例并使用该实例,还包含了一个构建该实例的 builder API。默认的 builder 可以通过 GraphQlSource.schemaResourceBuilder() 获取。
该 Boot Starter 会创建此构建器的实例,并进一步将其初始化为从可配置位置 加载模式文件,
暴露属性以应用于 GraphQlSource.Builder,检测
RuntimeWiringConfigurer Bean,
为 GraphQL 指标提供用于
插桩的 Bean,
以及用于 异常解析的 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 实例进行解析和合并。这意味着模式文件可以从几乎任何位置加载。
默认情况下,BootStarters会在classpath:graphql/**位置寻找扩展名为".graphqls"或".gqls"的模式文件,通常该位置是src/main/resources/graphql。你也可以使用文件系统位置,或者任何被Spring Resource层次结构支持的位置,包括从远程位置、存储中或内存中加载模式文件。
使用classpath*:graphql/**/来跨多个类路径位置查找模式文件,例如跨多个模块。 |
模式创建
默认情况下,GraphQlSource.Builder使用GraphQL Java SchemaGenerator来创建
graphql.schema.GraphQLSchema。这适用于典型用例,但如果您需要使用不同的生成器(例如用于Federation),可以注册一个schemaFactory回调:
GraphQlSource.Builder builder = ...
builder.schemaResources(..)
.configureRuntimeWiring(..)
.schemaFactory((typeDefinitionRegistry, runtimeWiring) -> {
// create GraphQLSchema
})
The GraphQlSource 部分 解释了如何使用 Spring Boot 进行配置。
对于 Apollo Federation 的示例,请参见 federation-jvm-spring-example。
RuntimeWiringConfigurer
您可以使用 RuntimeWiringConfigurer 进行注册:
-
自定义标量类型。
-
指令处理代码。
-
接口和联合类型的默认
TypeResolver。 -
DataFetcher用于字段,尽管应用程序通常会使用 注解控制器, 这些控制器会被AnnotatedControllerConfigurer检测并注册为DataFetchers, 而AnnotatedControllerConfigurer是一个RuntimeWiringConfigurer。Boot Starter 会自动注册AnnotatedControllerConfigurer。
| GraphQL Java,服务器应用仅使用Jackson进行数据到地图的数据序列化和反序列化。 客户端输入会被解析成一个映射。服务器输出会根据字段选择集组装成一个映射。 这意味着你不能依赖于Jackson的序列化/反序列化注解。 相反,你可以使用自定义标量类型。 |
The Boot Starter 检测类型为RuntimeWiringConfigurer 的 bean 并将其注册到GraphQlSource.Builder。这意味着大多数情况下,你的配置中会有如下内容:
@Configuration
public class GraphQlConfig {
@Bean
public RuntimeWiringConfigurer runtimeWiringConfigurer(BookRepository repository) {
GraphQLScalarType scalarType = ... ;
SchemaDirectiveWiring directiveWiring = ... ;
DataFetcher dataFetcher = QuerydslDataFetcher.builder(repository).single();
return wiringBuilder -> wiringBuilder
.scalar(scalarType)
.directiveWiring(directiveWiring)
.type("Query", builder -> builder.dataFetcher("book", dataFetcher));
}
}
如果需要添加一个WiringFactory,例如为了考虑模式定义进行注册,请实现替代的configure方法,该方法接受RuntimeWiring.Builder和输出List<WiringFactory>。这允许你添加任意数量的工厂,并依次调用这些工厂。
TypeResolver
GraphQlSource.Builder 将 ClassNameTypeResolver 注册为默认的 TypeResolver,用于那些尚未通过 RuntimeWiringConfigurer 进行此类注册的 GraphQL 接口和联合类型。在 GraphQL Java 中,TypeResolver 的作用是确定从 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);
The GraphQlSource 部分 解释了如何使用 Spring Boot 进行配置。
指令
GraphQL语言支持描述GraphQL文档中“替代运行时执行和类型验证行为”的指令。这些指令类似于Java中的注解,但在GraphQL文档中声明在类型、字段、片段和操作上。
GraphQL Java 提供了 SchemaDirectiveWiring 合约来帮助应用程序检测和处理指令。更多细节,请参见
Schema Directives 在
GraphQL Java 文档中。
在 Spring GraphQL 中,您可以通过 SchemaDirectiveWiring 注册一个 RuntimeWiringConfigurer。Boot Starter 会检测此类 Bean,因此您可能需要如下配置:
@Configuration
public class GraphQlConfig {
@Bean
public RuntimeWiringConfigurer runtimeWiringConfigurer() {
return builder -> builder.directiveWiring(new MySchemaDirectiveWiring());
}
}
| 对于指令支持的示例,请参阅 GraphQL Java 扩展验证库。 |
ExecutionStrategy
一个 ExecutionStrategy 在GraphQL Java中驱动请求字段的获取。
要创建一个 ExecutionStrategy,您需要提供一个 DataFetcherExceptionHandler。
默认情况下,Spring for GraphQL会创建异常处理器,并按照
异常处理 中所述使用该处理器设置在
GraphQL.Builder 上。GraphQL Java 然后使用这个配置的异常处理器来创建 AsyncExecutionStrategy 实例。
如果您需要创建一个自定义的ExecutionStrategy,您可以通过检测DataFetcherExceptionResolvers 并以相同的方式创建异常处理器来实现,并使用它来创建自定义的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,以便在创建后遍历和转换模式,并对模式进行更改。请注意,这比模式遍历更昂贵,因此通常除非需要对模式进行更改,否则请优先选择遍历来避免转换。
架构遍历
您可以通过builder.schemaResources(..).typeVisitors(..)注册一个graphql.schema.GraphQLTypeVisitor,以便在模式创建之后遍历该模式,并可能应用更改到GraphQLCodeRegistry。请注意,这样的访问者不能改变模式。如果您需要对模式进行更改,请参阅模式转换。
架构映射检查
如果查询、突变或订阅操作没有DataFetcher,它将不会返回任何数据,也不会做任何有用的事情。同样地,由操作返回的方案类型中的字段,除非通过显式的DataFetcher注册进行覆盖,否则也会被默认的PropertyDataFetcher忽略,后者会寻找匹配的Java对象属性,这些字段始终会是null。
GraphQL Java 不会检查每个模式字段是否都被覆盖,这可能会导致在测试覆盖率不足的情况下出现未发现的空缺。在运行时,你可能会得到一个“静默”的null,或者如果该字段不是可为空类型,则会出现错误。作为低级别的库,GraphQL Java 并不了解 DataFetcher 实现及其返回类型,因此无法将模式类型结构与 Java 对象结构进行比较。
Spring for GraphQL 定义了 SelfDescribingDataFetcher 接口,允许 DataFetcher 暴露返回类型信息。所有 Spring DataFetcher 实现都实现了此接口。这包括用于 注解控制器、Querydsl 以及 示例查询(Query by Example) Spring Data 存储库的实现。对于注解控制器,返回类型源自 @SchemaMapping 方法上声明的返回类型。
在启动时,Spring for GraphQL 可以检查模式字段、DataFetcher 注册和 DataFetcher 实现返回的 Java 对象属性,以验证所有模式字段是否都被显式注册的 DataFetcher 覆盖,或者与匹配的 Java 对象属性相匹配。此检查还执行反向检查,查找不存在于模式字段中的 DataFetcher 注册。
要启用模式映射检查:
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)
Skipped types: [BookOrAuthor] (3)
| 1 | 未映射的模式字段及其源类型列表 |
| 2 | 未找到字段的注册列表DataFetcher |
| 3 | 列出接下来将要跳过的模式类型 |
在某些情况下,模式字段检查的范围有限,尤其是在缺乏足够的Java类型信息时。例如,如果注解控制器方法声明返回java.lang.Object,或者返回类型具有未指定的泛型参数如List<?>,或者如果DataFetcher没有实现SelfDescribingDataFetcher且返回类型甚至未知,则情况就是这样。在这种情况下,Java对象类型的结构仍然未知,并且在生成的报告中将这些模式类型列为跳过处理。对于每个被跳过的类型,都会记录一条DEBUG级别的日志消息以说明为何跳过了该类型。
Schema联合类型总是会被跳过,因为无法在Java中声明此类返回类型,且Java的类型结构未知。
Schema接口类型仅支持直接声明的字段,这些字段与由SelfDescribingDataFetcher声明的Java类型上的属性进行比较。
额外实现的具体字段不会被检查。在未来版本中,可以改进以同时检查schema interface 实现类型,并尝试在声明的Java返回类型的子类型中找到匹配项。
操作缓存
GraphQL Java 在执行操作之前必须解析和验证该操作。这可能会显著影响性能。为了避免重复解析和验证,应用程序可以配置一个PreparsedDocumentProvider来缓存并复用 Document 实例。GraphQL Java 文档提供了通过PreparsedDocumentProvider进行查询缓存的更多详细信息。
在Spring GraphQL中,您可以注册一个PreparsedDocumentProvider到GraphQlSource.Builder#configureGraphQl:
// 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))
The GraphQlSource 部分 解释了如何使用 Spring Boot 进行配置。
线程模型
大多数GraphQL请求在获取嵌套字段时可以从并发执行中受益。这就是为什么今天的大多数应用程序都依赖于GraphQL Java的AsyncExecutionStrategy,它允许数据获取器返回CompletionStage并并发执行而不是串行执行。
Java 21 和虚拟线程增加了高效使用更多线程的能力,但仍然需要并发执行而不是顺序执行,以使请求执行更快完成。
Spring for GraphQL 支持:<br/>
-
响应式数据获取器,这些适应了
CompletionStage,这是AsyncExecutionStrategy所期望的。 -
CompletionStage作为返回值。 -
使用 Kotlin 协程方法的 Controller 方法。
-
@SchemaMapping 和 @BatchMapping 方法可以返回
Callable,并将其提交给Executor,例如 Spring Framework 的VirtualThreadTaskExecutor。要启用此功能,您必须在AnnotatedControllerConfigurer上配置一个Executor。
Spring for GraphQL 在 Spring MVC 或 WebFlux 之上运行作为传输方式。Spring MVC 使用异步请求执行,除非GraphQL Java引擎返回后生成的 CompletableFuture 立即完成,在这种情况下,如果请求足够简单且不需要异步数据获取,则会如此。
响应式DataFetcher
The default GraphQlSource builder 启用支持从DataFetcher返回Mono或Flux,这些会被适配为一个CompletableFuture,其中Flux值被聚合并转换成一个 List,除非请求是 GraphQL 订阅请求,在这种情况下,返回值保持为用于流式处理 GraphQL 响应的 Reactive Streams Publisher。
一个响应式的DataFetcher可以依赖于从传输层传播而来的Reactor上下文,例如来自WebFlux请求处理,请参阅
WebFlux 上下文。
上下文传播
Spring for GraphQL 提供了支持,可以在 HTTP 传输、GraphQL Java 和其调用的 DataFetcher 及其他组件之间透明地传递上下文。这包括来自 Spring MVC 请求处理线程的 ThreadLocal 上下文以及来自 WebFlux 处理管道的 Reactor Context。
Web MVC
A DataFetcher 以及其他由 GraphQL Java 调用的组件可能不会始终在与 Spring MVC 处理器相同的线程上执行,例如,如果异步
WebGraphQlInterceptor 或 DataFetcher 切换到不同的线程。
Spring for GraphQL 支持从 Servlet 容器线程向由 GraphQL Java 调用并执行的DataFetcher和其他组件传递ThreadLocal值。为此,应用程序需要为感兴趣的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决定如何在响应的"errors"部分表示数据获取时产生的异常。一个应用程序只能注册一个处理程序。
Spring for GraphQL 注册了一个 DataFetcherExceptionHandler,提供默认处理并启用 DataFetcherExceptionResolver 契约。应用程序可以通过 GraphQLSource 构建器注册任意数量的解析器,这些解析器按顺序执行,直到其中一个将 Exception 解析为 List<graphql.GraphQLError>。
Spring Boot Starter 会自动检测此类 Bean。
DataFetcherExceptionResolverAdapter 是一个方便的基类,包含受保护的方法 resolveToSingleError 和 resolveToMultipleErrors。
基于注解的控制器编程模型支持使用具有灵活方法签名的已注解异常处理方法来处理数据获取异常,详见
@GraphQlExceptionHandler以获取详细信息。
一个 GraphQLError 可以基于GraphQL Java 的 graphql.ErrorClassification 或者 Spring GraphQL 的 ErrorType 分配到一个类别中,其中定义了以下内容:
-
BAD_REQUEST -
UNAUTHORIZED -
FORBIDDEN -
NOT_FOUND -
INTERNAL_ERROR
如果一个异常未被解决,默认情况下它会被归类为一个INTERNAL_ERROR
,带有包含类别名称和从DataFetchingEnvironment获取的executionId的一个通用消息。该消息故意设计得模糊不清以避免泄露实现细节。应用程序可以使用DataFetcherExceptionResolver来自定义错误详情。
未解决的异常将以 ERROR 级别记录,并附带 executionId 以与发送给客户端的错误相关联。已解决的异常将以 DEBUG 级别记录。
请求异常
The GraphQL Java引擎在解析请求时可能会遇到验证或其他错误,这会导致请求执行被阻止。在这种情况下,响应包含一个"data"键以及一个或多个全局的"errors"(即没有字段路径),这些错误是请求级别的。
DataFetcherExceptionResolver 无法处理此类全局错误,因为这些错误在执行开始前抛出,且在调用任何 DataFetcher 之前发生。应用程序可以使用传输级拦截器来检查和转换 ExecutionResult 中的错误。
请查看 WebGraphQlInterceptor 下的示例。
订阅异常
订阅请求中的Publisher可能以错误信号完成,在这种情况下,底层传输(例如WebSocket)会发送一个最终的"error"类型消息,并附带GraphQL错误列表。
DataFetcherExceptionResolver 无法解决来自订阅 Publisher 的错误,
因为数据 DataFetcher 只是在初始化时创建了 Publisher。之后,传输会订阅可能随后因错误而完成的 Publisher。
一个应用程序可以注册一个SubscriptionExceptionResolver,以便解析来自订阅Publisher的异常,并将这些异常转换为GraphQL错误发送给客户端。
分页
The GraphQL 游标连接规范 defines一种方式来导航大结果集,通过每次返回一部分项,并且每项都配有一个客户端可以使用来请求更多在所引用项之前或之后的项的光标。
该规范将此模式称为
连接类型
Connection type 定义必须为需要分页的每种类型创建,这会增加模式中的样板代码和噪音。Spring for GraphQL 提供了 ConnectionTypeDefinitionConfigurer 来在启动时添加这些类型(如果它们不在解析后的 schema 文件中已经存在的话)。这意味着在模式中你只需要这个:
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
}
The Boot Starterregisters ConnectionTypeDefinitionConfigurer by default.
ConnectionAdapter
一旦在模式中可用 连接类型,您还需要相应的 Java 类型。GraphQL Java 提供了这些类型,包括通用的Connection和Edge,以及一个PageInfo。
一个选项是填充一个Connection并从控制器方法中返回它,或者DataFetcher。然而,这需要创建Connection的样板代码,生成游标,将每个项目包装为Edge,以及创建PageInfo。
此外,您可能已经在使用Spring Data repositories时有一个底层分页机制。
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 | 注册类型访问器。 |
Spring Data 的Window和Slice提供了内置的ConnectionAdapter。您也可以创建自己的自定义适配器。ConnectionAdapter的实现依赖于CursorStrategy来为返回的项目创建游标。相同的策略也用于支持包含分页输入的Subrange控制器方法参数。
CursorStrategy
CursorStrategy 是一个契约,用于编码和解码指向大结果集内项位置的字符串游标。游标可以基于索引也可以基于键值。
一个 ConnectionAdapter 使用此功能对返回项的光标进行编码。
注解控制器 方法、Querydsl 仓库以及 示例查询(Query by Example)
仓库使用它来解码分页请求中的光标,并创建 Subrange。
CursorEncoder 是一个相关的合约,进一步地对 String 游标进行编码和解码,使其对客户端是不透明的。EncodingCursorStrategy 结合了 CursorStrategy 和一个 CursorEncoder。您可以使用 Base64CursorEncoder、NoOpEncoder 或创建自己的版本。
存在一个CursorStrategyScrollPosition。 Boot Starter会在Spring Data存在时注册一个CursorStrategy<ScrollPosition>到Base64Encoder中。
批量加载
给定一个Book和其Author,我们可以为一本书创建一个DataFetcher,同时为其作者创建另一个DataFetcher。这使得可以选择带有或不带作者的书籍,但这也意味着书籍和作者不会一起加载,在查询多本书籍时,每个书籍的作者需要单独加载,这称为N+1次选择问题。
DataLoader
GraphQL Java 提供了一个 DataLoader 机制用于批量加载相关实体。
你可以在GraphQL Java 文档中找到全部详情。下面是一个
该机制工作原理的简要总结:
-
注册
DataLoader在可以加载实体的DataLoaderRegistry中,给定唯一键。 -
DataFetcher's 可以访问DataLoader's 并使用它们通过 ID 加载实体。 -
一个
DataLoader通过返回一个未来来推迟加载,因此可以在批处理中进行。 -
DataLoader's维护一个在每次请求中加载实体的缓存,这可以进一步提高效率。
BatchLoaderRegistry
The complete batching loading mechanism in GraphQL Java requires implementing one of
several BatchLoader interface, then wrapping and registering those as DataLoaders
with a name in the DataLoaderRegistry.
Spring GraphQL 的 API 稍微有些不同。注册时,只有一个中心的 GraphQLSchema 暴露工厂方法和一个构建器来创建并注册任意数量的批加载函数:
@Configuration
public class MyConfig {
public MyConfig(BatchLoaderRegistry registry) {
registry.forTypePair(Long.class, Author.class).registerMappedBatchLoader((authorIds, env) -> {
// return Mono<Map<Long, Author>
});
// more registrations ...
}
}
The Boot Starter 声明了一个 BatchLoaderRegistry Bean,你可以将其注入到你的配置中,如上所示,或者注入任何组件(例如控制器),以便注册批量加载功能。随后,BatchLoaderRegistry 被注入到 DefaultExecutionGraphQlService 中,在此确保每次请求的 DataLoader 注册。
默认情况下,DataLoader名称基于目标实体的类名。
这使得一个@SchemaMapping方法能够声明一个带有泛型类型的
DataLoader参数,
而无需指定名称。然而,如果需要的话,可以通过
BatchLoaderRegistry构建器自定义名称,并且还可以自定义其他DataLoaderOptions。
要将默认DataLoaderOptions全局配置为任何注册的起点,您可以重写 Boot 的 BatchLoaderRegistry 模块,并使用 DefaultBatchLoaderRegistry 接受 Supplier<DataLoaderOptions> 的构造函数。
对于许多情况,在加载相关实体时,你可以使用带有@BatchMapping注解的控制器方法,这相当于并替代了直接使用BatchLoaderRegistry和DataLoader的需求。
BatchLoaderRegistry 还提供了其他重要的好处。它支持从批量加载函数和 @BatchMapping 方法访问相同的 GraphQLContext,并且确保 Context Propagation 到这些地方。这也是为什么应用程序被期望使用它的原因。可以直接进行自己的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("...");
// ...