4. 请求执行
ExecutionGraphQlService
是调用 GraphQL Java 执行的主要 Spring 抽象
请求。基础传输(如 Server Transports)委托 to 来处理请求。ExecutionGraphQlService
主实现 配置了 用于访问要调用的实例。DefaultExecutionGraphQlService
GraphQlSource
graphql.GraphQL
4.1.GraphQLSource
GraphQlSource
是一个核心 Spring 抽象,用于访问用于请求执行的实例。它提供了一个构建器 API,用于
初始化 GraphQL Java 并构建一个 .graphql.GraphQL
GraphQlSource
默认构建器(可通过 访问)支持反应式 DataFetcher
、上下文传播和异常解决。GraphQlSource
GraphQlSource.schemaResourceBuilder()
Spring Boot Starters通过默认值初始化实例,并启用
以下内容:GraphQlSource
GraphQlSource.Builder
-
从可配置位置加载架构文件。
-
公开应用于 的属性。
GraphQlSource.Builder
-
检测
RuntimeWiringConfigurer
bean。 -
检测 GraphQL 指标的检测 Bean。
-
检测 bean 以解决异常。
DataFetcherExceptionResolver
-
检测 bean 以解决订阅异常。
SubscriptionExceptionResolver
对于进一步的自定义,您可以声明自己的 bean;
例如,要配置您自己的 :GraphQlSourceBuilderCustomizer
ExecutionIdProvider
@Configuration(proxyBeanMethods = false)
class GraphQlConfig {
@Bean
public GraphQlSourceBuilderCustomizer sourceBuilderCustomizer() {
return (builder) ->
builder.configureGraphQl(graphQlBuilder ->
graphQlBuilder.executionIdProvider(new CustomExecutionIdProvider()));
}
}
4.1.1. Schema 资源
GraphQlSource.Builder
可以配置一个或多个实例
解析并合并在一起。这意味着 schema 文件几乎可以从任何
位置。Resource
默认情况下, Spring Boot Starters会查找带有扩展名的 schema 文件
位置下的“.graphqls”或“.gqls”,通常为 .您还可以使用文件系统位置或任何位置
受 Spring 层次结构支持,包括一个自定义实现
从远程位置、存储或内存加载架构文件。classpath:graphql/**
src/main/resources/graphql
Resource
用于跨多个 Classpath 查找 schema 文件
位置,例如跨多个模块。classpath*:graphql/**/ |
4.1.2. Schema 创建
默认情况下,使用 GraphQL Java 来
创建 .这适用于大多数应用程序,但如果
必要时,您可以通过构建器挂接到 schema 创建中:GraphQlSource.Builder
GraphQLSchemaGenerator
graphql.schema.GraphQLSchema
GraphQlSource.Builder builder = ...
builder.schemaResources(..)
.configureRuntimeWiring(..)
.schemaFactory((typeDefinitionRegistry, runtimeWiring) -> {
// create GraphQLSchema
})
这样做的主要原因是通过联合库创建架构。
GraphQlSource 部分解释了如何使用 Spring Boot 进行配置。
4.1.3.RuntimeWiringConfigurer
您可以使用以下方法进行注册:RuntimeWiringConfigurer
-
自定义标量类型。
-
处理代码的指令。
-
TypeResolver
,如果需要覆盖类型的 DefaultTypeResolver
。 -
DataFetcher
对于字段,尽管大多数应用程序只会 configure ,它检测带注释的处理程序方法。 Spring Boot Starters默认添加AnnotatedControllerConfigurer
DataFetcher
AnnotatedControllerConfigurer
与 Web 框架不同,GraphQL 不使用 Jackson 注释来驱动 JSON 序列化/反序列化。 自定义数据类型及其序列化必须描述为 Scalars。 |
Spring Boot Starters检测 type 为
在 .这意味着在大多数情况下,您将拥有
类似于 this 的配置:RuntimeWiringConfigurer
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));
}
}
如果您需要添加 ,例如,进行注册时要考虑
模式定义中,实现同时接受 和 output 的替代方法。这允许您添加任何
然后按顺序调用的工厂数。WiringFactory
configure
RuntimeWiring.Builder
List<WiringFactory>
4.1.4. 默认TypeResolver
GraphQlSource.Builder
registers 作为默认值,用于尚未进行此类注册的 GraphQL 接口和联合
通过 RuntimeWiringConfigurer
。目的
a 用于确定值的 GraphQL 对象类型
从 for a GraphQL Interface 或 Union 字段返回。ClassNameTypeResolver
TypeResolver
TypeResolver
DataFetcher
ClassNameTypeResolver
尝试将值的简单类名与 GraphQL 匹配
Object 类型,如果不成功,它还会导航其超类型,包括
基类和接口,寻找匹配项。 提供了一个
选项来配置名称提取函数以及 GraphQL 对象类型
名称映射应该有助于涵盖更多极端情况:ClassNameTypeResolver
Class
GraphQlSource.Builder builder = ...
ClassNameTypeResolver classNameTypeResolver = new ClassNameTypeResolver();
classNameTypeResolver.setClassNameExtractor((klass) -> {
// Implement Custom ClassName Extractor here
});
builder.defaultTypeResolver(classNameTypeResolver);
GraphQlSource 部分解释了如何使用 Spring Boot 进行配置。
4.1.5. 操作缓存
GraphQL Java 必须在执行操作之前对其进行解析和验证。这可能会影响
性能显着。为避免需要重新分析和验证,应用程序可以
配置 a 来缓存和重用 Document 实例。GraphQL Java 文档提供了有关以下内容的更多详细信息
查询缓存。PreparsedDocumentProvider
PreparsedDocumentProvider
在 Spring GraphQL 中,您可以通过:
.PreparsedDocumentProvider
GraphQlSource.Builder#configureGraphQl
// Typically, accessed through Spring Boot's GraphQlSourceBuilderCustomizer
GraphQlSource.Builder builder = ...
// Create provider
PreparsedDocumentProvider provider = ...
builder.schemaResources(..)
.configureRuntimeWiring(..)
.configureGraphQl(graphQLBuilder -> graphQLBuilder.preparsedDocumentProvider(provider))
GraphQlSource 部分解释了如何使用 Spring Boot 进行配置。
4.1.6. 指令
GraphQL 语言支持“描述替代运行时执行和 GraphQL 文档中的类型验证行为”。指令类似于 Java,但在 GraphQL 文档中的类型、字段、片段和操作上声明。
GraphQL Java 提供了帮助应用程序检测的协定
和 handle 指令。有关更多详细信息,请参阅
GraphQL Java 文档。SchemaDirectiveWiring
在 Spring GraphQL 中,你可以通过RuntimeWiringConfigurer
注册一个。Spring Boot Starters检测到
这样的 bean,所以你可能会有这样的东西:SchemaDirectiveWiring
@Configuration
public class GraphQlConfig {
@Bean
public RuntimeWiringConfigurer runtimeWiringConfigurer() {
return builder -> builder.directiveWiring(new MySchemaDirectiveWiring());
}
}
有关指令支持的示例,请查看 Graphql Java 库的扩展验证。 |
4.2. 反应式DataFetcher
默认构建器支持返回 a 或将其调整为 where 值聚合
并转换为 List,除非请求是 GraphQL 订阅请求,
在这种情况下,返回值仍然是用于流式处理的 Reactive Streams
GraphQL 响应。GraphQlSource
DataFetcher
Mono
Flux
CompletableFuture
Flux
Publisher
响应式可以依赖从
传输层,例如从 WebFlux 请求处理,请参阅 WebFlux 上下文。DataFetcher
4.3. 上下文传播
Spring for GraphQL 支持通过 GraphQL Java 透明地将上下文从服务器传输传播到它的其他组件
调用。这包括来自 Spring MVC 请求处理的上下文
thread 和 Reactor 从 WebFlux 处理管道。DataFetcher
ThreadLocal
Context
4.3.1. WebMvc
GraphQL Java 调用的 A 和其他组件可能并不总是在
与 Spring MVC 处理程序相同的线程,例如,如果异步 WebGraphQlInterceptor
或切换到
不同的线程。DataFetcher
DataFetcher
Spring for GraphQL 支持从 Servlet 容器传播值
线程添加到线程 a 和其他组件中,GraphQL Java 调用到
执行时间。为此,应用程序需要创建一个来提取感兴趣的值:ThreadLocal
DataFetcher
ThreadLocalAccessor
ThreadLocal
public class RequestAttributesAccessor implements ThreadLocalAccessor {
private static final String KEY = RequestAttributesAccessor.class.getName();
@Override
public void extractValues(Map<String, Object> container) {
container.put(KEY, RequestContextHolder.getRequestAttributes());
}
@Override
public void restoreValues(Map<String, Object> values) {
if (values.containsKey(KEY)) {
RequestContextHolder.setRequestAttributes((RequestAttributes) values.get(KEY));
}
}
@Override
public void resetValues(Map<String, Object> values) {
RequestContextHolder.resetRequestAttributes();
}
}
A 可以在 WebGraphHandler 构建器中注册。Boot Starters会检测此类型的 bean 并自动为
Spring MVC 应用程序,请参阅 Web 端点部分。ThreadLocalAccessor
4.3.2. WebFlux 网络
响应式 DataFetcher
可以依赖于对 Reactor 上下文的访问,该上下文
源自 WebFlux 请求处理链。这包括 Reactor 上下文
由 WebGraphQlInterceptor 组件添加。
4.4. 异常解决
GraphQL Java 应用程序可以注册 a 来决定如何
表示 GraphQL 响应的 “errors” 部分中数据层的异常。DataFetcherExceptionHandler
Spring for GraphQL 有一个内置功能,该内置功能已配置为使用
通过默认的 GraphQLSource
构建器。它允许应用程序注册
按顺序调用的一个或多个 Spring 组件
直到将 解析为对象列表(可能为空)。DataFetcherExceptionHandler
DataFetcherExceptionResolver
Exception
graphql.GraphQLError
DataFetcherExceptionResolver
是一个异步合约。对于大多数实现,它
就足以扩展和覆盖
其 or 方法之一
同步解决异常。DataFetcherExceptionResolverAdapter
resolveToSingleError
resolveToMultipleErrors
可以通过 A 分配给类别。
在 Spring GraphQL 中,您还可以通过 which 分配具有以下常见
应用程序可用于对错误进行分类的分类:GraphQLError
graphql.ErrorClassification
ErrorType
-
BAD_REQUEST
-
UNAUTHORIZED
-
FORBIDDEN
-
NOT_FOUND
-
INTERNAL_ERROR
如果异常仍未解决,则默认情况下,该异常被归类为包含类别名称和 from 的通用消息。该消息故意不透明以避免泄漏
实现细节。应用程序可以使用 进行自定义
错误详细信息。INTERNAL_ERROR
executionId
DataFetchingEnvironment
DataFetcherExceptionResolver
未解决的异常将记录在 ERROR 级别以及 to correlate
发送到客户端的错误。已解决的异常记录在 DEBUG 级别。executionId
4.4.1. 请求异常
GraphQL Java 引擎在解析请求时可能会遇到验证或其他错误
这反过来又会阻止请求执行。在这种情况下,响应包含
“data” 键,以及一个或多个请求级别的 “错误”,这些错误是全局的,即不是
具有字段路径。null
DataFetcherExceptionResolver
无法处理此类全局错误,因为它们是引发的
在执行开始之前和调用 any 之前。应用程序可以使用
传输级拦截器来检查和转换 .
请参阅 WebGraphQlInterceptor
下的示例。DataFetcher
ExecutionResult
4.4.2. 订阅例外
的 for a subscription 请求可能会完成并显示错误信号,在这种情况下
底层传输(例如 WebSocket)发送带有列表的最终 “error” 类型消息
的 GraphQL 错误。Publisher
DataFetcherExceptionResolver
无法解决订阅 中的错误
由于数据仅创建初始。之后,
transport 订阅 ,然后可能会完成并显示错误。Publisher
DataFetcher
Publisher
Publisher
应用程序可以注册 a 以解析
exceptions 来解决这些问题为 GraphQL 错误
以发送到客户端。SubscriptionExceptionResolver
Publisher
4.5. 批量加载
给定 a 及其 ,我们可以为一本书创建一个 和 另一个
对于它的作者。这允许选择有作者或无作者的书籍,但这意味着书籍
和 authors 不会一起加载,这在查询多个
books 作为每本书的作者是单独加载的。这称为 N+1 选择
问题。Book
Author
DataFetcher
4.5.1.DataLoader
GraphQL Java 提供了一种批量加载相关实体的机制。
您可以在 GraphQL Java 文档中找到完整的详细信息。下面是一个
工作原理摘要:DataLoader
-
Register 的 ,可以加载实体,给定唯一键。
DataLoader
DataLoaderRegistry
-
DataFetcher
可以访问 并使用它们按 ID 加载实体。DataLoader
-
A 通过返回 future 来延迟加载,以便可以批量完成。
DataLoader
-
DataLoader
维护加载实体的每个请求缓存,该缓存可以进一步 提高效率。
4.5.2.BatchLoaderRegistry
GraphQL Java 中的完整批处理加载机制需要实现以下之一
several 接口,然后将它们包装并注册为 S
名称位于 .BatchLoader
DataLoader
DataLoaderRegistry
Spring GraphQL 中的 API 略有不同。对于注册,只有一个
Central 公开工厂方法和用于 create 和
注册任意数量的批量加载函数: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 ...
}
}
Spring Boot Starters声明了一个可以注入的 bean
您的配置,如上所示,或按顺序放入任何组件(如控制器)
注册批量加载函数。反过来,它被注入到确保每个请求的注册的位置。BatchLoaderRegistry
BatchLoaderRegistry
DefaultExecutionGraphQlService
DataLoader
默认情况下,该名称基于目标实体的类名。
这允许方法使用泛型类型声明 DataLoader 参数,并且
无需指定名称。但是,如有必要,可以通过构建器以及其他选项自定义名称。DataLoader
@SchemaMapping
BatchLoaderRegistry
DataLoader
在许多情况下,在加载相关实体时,您可以使用 @BatchMapping 控制器方法,这是一种快捷方式
for 和 replace 需要使用 和 直接。
S 还提供其他重要的好处。它支持访问
与 batch loading 函数和 from 方法相同,
以及确保对它们的上下文传播。这就是预期应用的原因
以使用它。可以直接执行自己的注册,但
此类注册将放弃上述好处。BatchLoaderRegistry
DataLoader
BatchLoaderRegistry
GraphQLContext
@BatchMapping
DataLoader
4.5.3. 测试 Batch Load
首先在 上执行注册 :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("...");
// ...