带注解的控制器
Spring for GraphQL 提供了一个基于注解的编程模型,其中 @Controller 组件使用注解来声明具有灵活方法签名的数据处理方法,以获取特定 GraphQL 字段的数据。例如
@Controller
public class GreetingController {
@QueryMapping (1)
public String hello() { (2)
return "Hello, world!";
}
}
| 1 | 将此方法绑定到查询,即 Query 类型下的字段。 |
| 2 | 如果注解中未声明查询,则从方法名称确定查询。 |
Spring for GraphQL 使用 RuntimeWiring.Builder 将上述处理方法注册为名为 "hello" 的查询的 graphql.schema.DataFetcher。
声明
您可以将 @Controller bean 定义为标准的 Spring bean 定义。 @Controller 注解允许自动检测,与 Spring 对类路径上 @Controller 和 @Component 类的通用检测和为其自动注册 bean 定义的支持保持一致。它还作为注解类的原型,表明其在 GraphQL 应用程序中作为数据获取组件的角色。
AnnotatedControllerConfigurer 检测 @Controller bean 并通过 RuntimeWiring.Builder 将其注解的处理方法注册为 DataFetcher。它是 RuntimeWiringConfigurer 的实现,可以添加到 GraphQlSource.Builder。 Boot Starter 自动将 AnnotatedControllerConfigurer 声明为一个 bean,并将所有 RuntimeWiringConfigurer bean 添加到 GraphQlSource.Builder,从而启用对注解 DataFetcher 的支持,请参阅 Boot Starter 文档中的 GraphQL RuntimeWiring 部分。
@SchemaMapping
@SchemaMapping 注解将处理方法映射到 GraphQL 模式中的字段,并声明它为该字段的 DataFetcher。该注解可以指定父类型名称和字段名称
@Controller
public class BookController {
@SchemaMapping(typeName="Book", field="author")
public Author getAuthor(Book book) {
// ...
}
}
@SchemaMapping 注解也可以省略这些属性,在这种情况下,字段名称默认为方法名称,而类型名称默认为注入到方法中的源/父对象的简单类名。例如,以下默认为类型“Book”和字段“author”
@Controller
public class BookController {
@SchemaMapping
public Author author(Book book) {
// ...
}
}
@SchemaMapping 注解可以在类级别声明,以指定类中所有处理方法的默认类型名称。
@Controller
@SchemaMapping(typeName="Book")
public class BookController {
// @SchemaMapping methods for fields of the "Book" type
}
@QueryMapping、@MutationMapping 和 @SubscriptionMapping 是元注解,它们本身被 @SchemaMapping 注解,并且 typeName 分别预设为 Query、Mutation 或 Subscription。实际上,这些是 Query、Mutation 和 Subscription 类型下字段的快捷注解。例如
@Controller
public class BookController {
@QueryMapping
public Book bookById(@Argument Long id) {
// ...
}
@MutationMapping
public Book addBook(@Argument BookInput bookInput) {
// ...
}
@SubscriptionMapping
public Flux<Book> newPublications() {
// ...
}
}
@SchemaMapping 处理方法具有灵活的签名,并且可以选择各种方法参数和返回值。
方法参数
模式映射处理方法可以具有以下任何方法参数
| 方法参数 | 描述 |
|---|---|
|
用于访问绑定到更高级别、类型化对象的命名字段参数。 请参阅 |
|
用于访问原始参数值。 请参阅 |
|
用于访问绑定到更高级别、类型化对象的命名字段参数,以及一个标志以指示输入参数是省略还是设置为 请参阅 |
|
用于访问绑定到更高级别、类型化对象的所有字段参数。 请参阅 |
|
用于访问原始参数映射。 |
|
通过投影接口访问字段参数。 请参阅 |
"Source" |
用于访问字段的源(即父/容器)实例。 请参阅 Source。 |
|
用于访问分页参数。 |
|
用于访问排序详情。 |
|
用于访问 请参阅 |
|
用于从 |
|
用于从 |
|
用于从 |
|
如果可用,则从 Spring Security 上下文获取。 |
|
用于从 Spring Security 上下文访问 |
|
通过 |
|
用于从 |
|
用于直接访问底层 |
返回值
模式映射处理方法可以返回
| 返回值 | 描述 |
|---|---|
已解析的值 |
直接解析的任何应用程序类型。 |
|
用于异步值。 支持控制器方法和任何 |
Kotlin |
它们会自动适配到 |
|
用于异步生成值。为此, |
|
其中 这是“完整”的 GraphQL Java 返回值,不仅包含“数据”。用于通过“扩展”或 “本地上下文” 完成结果。 |
在 Java 21+ 上,当 AnnotatedControllerConfigurer 配置了 Executor 时,具有阻塞方法签名的控制器方法会异步调用。默认情况下,如果控制器方法不返回异步类型(如 Flux、Mono、CompletableFuture),并且也不是 Kotlin 挂起函数,则认为它是阻塞的。您可以在 AnnotatedControllerConfigurer 上配置一个阻塞控制器方法 Predicate 以帮助确定哪些方法被认为是阻塞的。
当属性 spring.threads.virtual.enabled 设置时,Spring for GraphQL 的 Spring Boot 启动器会自动为虚拟线程配置 AnnotatedControllerConfigurer 和 Executor。 |
接口模式映射
当控制器方法映射到模式接口字段时,默认情况下,该映射将被多个映射替换,每个实现该接口的模式对象类型一个。这允许对所有子类型使用一个控制器方法。
例如,给定
type Query {
activities: [Activity!]!
}
interface Activity {
id: ID!
coordinator: User!
}
type FooActivity implements Activity {
id: ID!
coordinator: User!
}
type BarActivity implements Activity {
id: ID!
coordinator: User!
}
type User {
name: String!
}
你可以这样编写一个控制器
@Controller
public class ActivityController {
@QueryMapping
public List<Activity> activities() {
// ...
}
@SchemaMapping
public User coordinator(Activity activity) {
// Called for any Activity subtype
}
}
如有必要,您可以接管单个子类型的映射
@Controller
public class ActivityController {
@QueryMapping
public List<Activity> activities() {
// ...
}
@SchemaMapping
public User coordinator(Activity activity) {
// Called for any Activity subtype except FooActivity
}
@SchemaMapping
public User coordinator(FooActivity activity) {
// ...
}
}
@Argument
在 GraphQL Java 中,DataFetchingEnvironment 提供对字段特定参数值映射的访问。这些值可以是简单的标量值(例如 String、Long),用于更复杂输入的 Map 值,或 List 值。
使用 @Argument 注解将参数绑定到目标对象并注入到处理方法中。如果目标对象不是简单的 Java 类型,则通过主数据构造函数执行绑定,并通过使用嵌套参数值递归重复此操作,以调用嵌套目标对象的构造函数。例如
@Controller
public class BookController {
@QueryMapping
public Book bookById(@Argument Long id) {
// ...
}
@MutationMapping
public Book addBook(@Argument BookInput bookInput) {
// ...
}
}
或者,也可以通过首先调用默认构造函数,然后通过目标对象上的 setter 应用参数值来完成绑定。
| 如果目标对象没有 setter,您可以配置参数绑定器以回退到通过直接字段访问进行绑定,请参阅 参数绑定。 |
默认情况下,如果方法参数名称可用(例如在 Java 8+ 中使用 -parameters 编译器标志或来自编译器的调试信息),则它用于查找参数。如有必要,您可以通过注解自定义名称,例如 @Argument("bookInput")。
请注意,@Argument 注解没有“required”标志,也没有指定默认值的选项。这两者都可以在 GraphQL 模式级别指定,并由 GraphQL Java 强制执行。
如果绑定失败,将抛出 BindException,并将绑定问题作为字段错误累积,其中每个错误的 field 是发生问题的参数路径。
您可以将 @Argument 与 Map<String, Object> 参数一起使用,以获取参数的原始值。例如
@Controller
public class BookController {
@MutationMapping
public Book addBook(@Argument Map<String, Object> bookInput) {
// ...
}
}
在 1.2 之前,当注解未指定名称时,@Argument Map<String, Object> 返回完整的参数映射。在 1.2 之后,@Argument Map<String, Object> 始终返回原始参数值,无论是基于参数名称还是注解中的名称。要访问完整的参数映射,请改用 @Arguments。 |
参数绑定
@Argument 绑定的支持由 GraphQlArgumentBinder 提供。此类的作用是从 GraphQL 参数值创建和填充目标对象。
参数绑定支持多种自定义选项
-
nameResolver— 自定义 GraphQL 参数名称到对象属性的映射,这对于处理命名约定(如使用“-”)很有用。 -
fallBackOnDirectFieldAccess— 如果目标对象不使用访问器方法,则回退到直接字段访问。 -
conversionService— 在需要时用于类型转换。
要自定义参数绑定,请使用 AnnotatedControllerConfigurer 上的专用属性来设置 GraphQlArgumentBinder.Options。
ArgumentValue
默认情况下,GraphQL 中的输入参数是可空且可选的,这意味着参数可以设置为 null 字面量,也可以根本不提供。这种区别对于使用突变的局部更新很有用,其中底层数据也可以相应地设置为 null 或根本不更改。当使用 @Argument 时,无法进行这种区分,因为在这两种情况下都会得到 null 或空 Optional。
如果您想知道某个值是否根本未提供,您可以声明一个 ArgumentValue 方法参数,它是一个简单的容器,用于存储结果值,以及一个标志来指示输入参数是否完全省略。您可以将其替代 @Argument 使用,在这种情况下,参数名称是从方法参数名称确定的,或者与 @Argument 一起使用以指定参数名称。
例如:
@Controller
public class BookController {
@MutationMapping
public void addBook(ArgumentValue<BookInput> bookInput) {
if (!bookInput.isOmitted()) {
BookInput value = bookInput.value();
// ...
}
}
}
ArgumentValue 也支持作为 @Argument 方法参数的对象结构中的字段,无论是通过构造函数参数还是通过 setter 初始化,包括作为顶层对象以下任何级别的嵌套对象的字段。
客户端也支持此功能,带有专用的 Jackson 模块,请参阅 客户端的 ArgumentValue 支持 部分。
@Arguments
如果您想将完整的参数映射绑定到一个目标对象,请使用 @Arguments 注解,这与绑定特定命名参数的 @Argument 不同。
例如,@Argument BookInput bookInput 使用参数“bookInput”的值来初始化 BookInput,而 @Arguments 使用完整的参数映射,在这种情况下,顶层参数绑定到 BookInput 属性。
您可以将 @Arguments 与 Map<String, Object> 参数一起使用,以获取所有参数值的原始映射。
@ProjectedPayload 接口
作为使用 @Argument 的完整对象的替代方案,您还可以使用投影接口通过定义良好的最小接口访问 GraphQL 请求参数。当 Spring Data 位于类路径上时,参数投影由 Spring Data 的接口投影 提供。
要使用此功能,请创建一个带有 @ProjectedPayload 注解的接口,并将其声明为控制器方法参数。如果参数用 @Argument 注解,它将应用于 DataFetchingEnvironment.getArguments() 映射中的单个参数。当不带 @Argument 声明时,投影作用于完整参数映射中的顶层参数。
例如:
@Controller
public class BookController {
@QueryMapping
public Book bookById(BookIdProjection bookId) {
// ...
}
@MutationMapping
public Book addBook(@Argument BookInputProjection bookInput) {
// ...
}
}
@ProjectedPayload
interface BookIdProjection {
Long getId();
}
@ProjectedPayload
interface BookInputProjection {
String getName();
@Value("#{target.author + ' ' + target.name}")
String getAuthorAndName();
}
Source
在 GraphQL Java 中,DataFetchingEnvironment 提供对字段的源(即父/容器)实例的访问。要访问此实例,只需声明一个预期目标类型的方法参数即可。
@Controller
public class BookController {
@SchemaMapping
public Author author(Book book) {
// ...
}
}
源方法参数还有助于确定映射的类型名称。如果 Java 类的简单名称与 GraphQL 类型匹配,则无需在 @SchemaMapping 注解中显式指定类型名称。
|
一个 |
Subrange
当 Spring 配置中存在 CursorStrategy bean 时,控制器方法支持 Subrange<P> 参数,其中 <P> 是从游标转换的相对位置。对于 Spring Data,ScrollSubrange 公开 ScrollPosition。例如
@Controller
public class BookController {
@QueryMapping
public Window<Book> books(ScrollSubrange subrange) {
ScrollPosition position = subrange.position().orElse(ScrollPosition.offset());
int count = subrange.count().orElse(20);
// ...
}
}
请参阅 分页 以获取分页和内置机制的概述。
Sort
当 Spring 配置中存在 SortStrategy bean 时,控制器方法支持 Sort 作为方法参数。例如
@Controller
public class BookController {
@QueryMapping
public Window<Book> books(Optional<Sort> optionalSort) {
Sort sort = optionalSort.orElse(Sort.by(..));
}
}
DataLoader
当您为实体注册批量加载函数时,如 批量加载 中所解释的,您可以通过声明 DataLoader 类型的方法参数来访问实体的 DataLoader 并使用它来加载实体
@Controller
public class BookController {
public BookController(BatchLoaderRegistry registry) {
registry.forTypePair(Long.class, Author.class).registerMappedBatchLoader((authorIds, env) -> {
// return Map<Long, Author>
});
}
@SchemaMapping
public CompletableFuture<Author> author(Book book, DataLoader<Long, Author> loader) {
return loader.load(book.getAuthorId());
}
}
默认情况下,BatchLoaderRegistry 使用值类型的完整类名(例如 Author 的类名)作为注册的键,因此简单地声明带有泛型类型的 DataLoader 方法参数提供了足够的信息来在 DataLoaderRegistry 中定位它。作为回退,DataLoader 方法参数解析器还将尝试方法参数名称作为键,但通常这应该不是必需的。
请注意,对于许多加载相关实体的情况,其中 @SchemaMapping 只是委托给 DataLoader,您可以通过使用 @BatchMapping 方法来减少样板代码,如下一节所述。
验证
当找到 javax.validation.Validator bean 时,AnnotatedControllerConfigurer 为注解控制器方法启用 Bean Validation 支持。通常,bean 的类型是 LocalValidatorFactoryBean。
Bean 验证允许您声明类型上的约束
public class BookInput {
@NotNull
private String title;
@NotNull
@Size(max=13)
private String isbn;
}
然后,您可以使用 @Valid 注解控制器方法参数,以便在方法调用之前对其进行验证
@Controller
public class BookController {
@MutationMapping
public Book addBook(@Argument @Valid BookInput bookInput) {
// ...
}
}
如果在验证过程中发生错误,将抛出 ConstraintViolationException。您可以使用 异常 链来决定如何通过将其转换为要包含在 GraphQL 响应中的错误来呈现给客户端。
除了 @Valid,您还可以使用 Spring 的 @Validated,它允许指定验证组。 |
Bean 验证对于 @Argument、@Arguments 和 @ProjectedPayload 方法参数很有用,但更普遍适用于任何方法参数。
|
验证和 Kotlin 协程
Hibernate Validator 与 Kotlin 协程方法不兼容,在内省其方法参数时会失败。请参阅 spring-projects/spring-graphql#344 (comment) 以获取相关问题链接和建议的解决方法。 |
HTTP 头
要从控制器方法访问 HTTP 头,我们建议使用 WebGraphQlInterceptor 将感兴趣的 HTTP 头复制到 GraphQLContext 中,从那里它们可以用于任何 DataFetcher,以及作为 @GraphQlContext 参数的注解控制器方法。
有一个内置的 HttpRequestHeaderInterceptor 可以帮助实现这一点。请参阅 Web 拦截器。
本地上下文
主 GraphQlContext 对整个查询是全局的,可用于存储和检索用于可观察性、安全性等方面的跨领域上下文数据。有时您希望将附加信息传递给子字段数据获取器,并避免污染主上下文。对于此类用例,您应考虑使用本地 GraphQLContext,因为它仅限于数据获取操作的子集。
控制器方法可以通过返回包含已解析数据和新上下文的 DataFetcherResult<T> 来提供本地上下文
@Controller
public class LocalContextBookController {
@QueryMapping
public DataFetcherResult<Book> bookById(@Argument Long id) {
// Our controller method must return a DataFetcherResult
DataFetcherResult.Builder<Book> resultBuilder = DataFetcherResult.newResult();
BookAndAuthor bookAndAuthor = this.fetchBookAndAuthorById(id);
// Create a new local context and store the author value
GraphQLContext localContext = GraphQLContext.getDefault()
.put("author", bookAndAuthor.author);
return resultBuilder
.data(bookAndAuthor.book)
.localContext(localContext)
.build();
}
@SchemaMapping
public List<Book> related(Book book, @LocalContextValue Author author) {
List<Book> relatedBooks = new ArrayList<>();
relatedBooks.addAll(fetchBooksByAuthor(author));
relatedBooks.addAll(fetchSimilarBooks(book));
return relatedBooks;
}
如果您想查看更多详细的示例和讨论,请参阅 graphql-java 文档中“通过前瞻构建高效数据获取器”部分。
@BatchMapping
批量加载 通过使用 org.dataloader.DataLoader 解决了 N+1 选择问题,以延迟加载单个实体实例,以便它们可以一起加载。例如
@Controller
public class BookController {
public BookController(BatchLoaderRegistry registry) {
registry.forTypePair(Long.class, Author.class).registerMappedBatchLoader((authorIds, env) -> {
// return Map<Long, Author>
});
}
@SchemaMapping
public CompletableFuture<Author> author(Book book, DataLoader<Long, Author> loader) {
return loader.load(book.getAuthorId());
}
}
对于上面所示的加载关联实体的直接情况,@SchemaMapping 方法除了委托给 DataLoader 之外别无他法。这是一种可以使用 @BatchMapping 方法避免的样板代码。例如
@Controller
public class BookController {
@BatchMapping
public Mono<Map<Book, Author>> author(List<Book> books) {
// ...
}
}
上述方法变为 BatchLoaderRegistry 中的批量加载函数,其中键是 Book 实例,加载的值是其作者。此外,一个 DataFetcher 也透明地绑定到 Book 类型的 author 字段,它只是将请求委托给作者的 DataLoader,给定其源/父 Book 实例。
|
要用作唯一键, |
默认情况下,字段名称默认为方法名称,而类型名称默认为输入 List 元素类型的简单类名。两者都可以通过注解属性进行自定义。类型名称也可以从类级别的 @SchemaMapping 继承。
|
|
方法参数
批量映射方法支持以下参数
| 方法参数 | 描述 |
|---|---|
|
源/父对象。 |
|
如果可用,则从 Spring Security 上下文获取。 |
|
用于从 |
|
用于从 |
|
GraphQL Java 中可用于
|
返回值
批量映射方法可以返回
| 返回类型 | 描述 |
|---|---|
|
一个以父对象为键,批量加载对象为值的映射。 |
|
一个批量加载对象的序列,其顺序必须与传递给方法的源/父对象的顺序相同。 |
|
命令式变体,例如没有远程调用的情况。 |
|
命令式变体,异步调用。为此, |
带有 |
适配为 |
在 Java 21+ 上,当 AnnotatedControllerConfigurer 配置了 Executor 时,具有阻塞方法签名的控制器方法会异步调用。默认情况下,如果控制器方法不返回异步类型(如 Flux、Mono、CompletableFuture),并且也不是 Kotlin 挂起函数,则认为它是阻塞的。您可以在 AnnotatedControllerConfigurer 上配置一个阻塞控制器方法 Predicate 以帮助确定哪些方法被认为是阻塞的。
当属性 spring.threads.virtual.enabled 设置时,Spring for GraphQL 的 Spring Boot 启动器会自动为虚拟线程配置 AnnotatedControllerConfigurer 和 Executor。 |
接口批量映射
与 接口模式映射 一样,当批量映射方法映射到模式接口字段时,该映射将被多个映射替换,每个实现该接口的模式对象类型一个。
这意味着,给定以下内容
type Query {
activities: [Activity!]!
}
interface Activity {
id: ID!
coordinator: User!
}
type FooActivity implements Activity {
id: ID!
coordinator: User!
}
type BarActivity implements Activity {
id: ID!
coordinator: User!
}
type User {
name: String!
}
你可以这样编写一个控制器
@Controller
public class BookController {
@QueryMapping
public List<Activity> activities() {
// ...
}
@BatchMapping
Map<Activity, User> coordinator(List<Activity> activities) {
// Called for all Activity subtypes
}
}
如有必要,您可以接管单个子类型的映射
@Controller
public class BookController {
@QueryMapping
public List<Activity> activities() {
// ...
}
@BatchMapping
Map<Activity, User> coordinator(List<Activity> activities) {
// Called for all Activity subtypes
}
@BatchMapping(field = "coordinator")
Map<Activity, User> fooCoordinator(List<FooActivity> activities) {
// ...
}
}
@GraphQlExceptionHandler
使用 @GraphQlExceptionHandler 方法处理数据获取中的异常,并具有灵活的 方法签名。当在控制器中声明时,异常处理方法适用于来自同一控制器的异常
import graphql.GraphQLError;
import graphql.GraphqlErrorBuilder;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.GraphQlExceptionHandler;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.graphql.execution.ErrorType;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindException;
@Controller
public class BookController {
@QueryMapping
public Book bookById(@Argument Long id) {
return ...
}
@GraphQlExceptionHandler
public GraphQLError handle(GraphqlErrorBuilder<?> errorBuilder, BindException ex) {
return errorBuilder
.errorType(ErrorType.BAD_REQUEST)
.message(ex.getMessage())
.build();
}
}
当在 @ControllerAdvice 中声明时,异常处理方法适用于所有控制器
import graphql.GraphQLError;
import graphql.GraphqlErrorBuilder;
import org.springframework.graphql.data.method.annotation.GraphQlExceptionHandler;
import org.springframework.graphql.execution.ErrorType;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ControllerAdvice;
@ControllerAdvice
public class GlobalExceptionHandler {
@GraphQlExceptionHandler
public GraphQLError handle(GraphqlErrorBuilder<?> errorBuilder, BindException ex) {
return errorBuilder
.errorType(ErrorType.BAD_REQUEST)
.message(ex.getMessage())
.build();
}
}
如上例所示,您应该通过在方法签名中注入 GraphQlErrorBuilder 来构建错误,因为它已准备好当前的 DataFetchingEnvironment。
通过 @GraphQlExceptionHandler 方法进行异常处理会自动应用于控制器调用。要处理来自其他 graphql.schema.DataFetcher 实现(非基于控制器方法)的异常,请从 AnnotatedControllerConfigurer 获取 DataFetcherExceptionResolver,并将其注册到 GraphQlSource.Builder 中作为 DataFetcherExceptionResolver。
方法签名
异常处理方法支持灵活的方法签名,其方法参数从 DataFetchingEnvironment 解析,并与 @SchemaMapping 方法 的参数匹配。
支持的返回类型如下
| 返回类型 | 描述 |
|---|---|
|
将异常解析为单个字段错误。 |
|
将异常解析为多个字段错误。 |
|
解析异常,不带响应错误。 |
|
将异常解析为单个错误、多个错误或无错误。返回值必须是 |
|
用于异步解析,其中 |
命名空间
在模式级别,查询和突变操作直接在 Query 和 Mutation 类型下定义。丰富的 GraphQL API 可以在这些类型下定义数十个操作,从而使其更难以探索 API 和分离关注点。您可以选择 在 GraphQL 模式中定义命名空间。尽管这种方法存在一些注意事项,但您可以使用 Spring for GraphQL 注解控制器实现此模式。
使用命名空间,您的 GraphQL 模式可以将查询操作嵌套在顶级类型下,而不是直接在 Query 下列出。在这里,我们将定义 MusicQueries 和 UserQueries 类型,并使它们在 Query 下可用
type Query {
music: MusicQueries
users: UserQueries
}
type MusicQueries {
album(id: ID!): Album
searchForArtist(name: String!): [Artist]
}
type Album {
id: ID!
title: String!
}
type Artist {
id: ID!
name: String!
}
type UserQueries {
user(login: String): User
}
type User {
id: ID!
login: String!
}
GraphQL 客户端将像这样使用 album 查询
{
music {
album(id: 42) {
id
title
}
}
}
并得到以下响应
{
"data": {
"music": {
"album": {
"id": "42",
"title": "Spring for GraphQL"
}
}
}
}
这可以在 @Controller 中使用以下模式实现
import java.util.List;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.graphql.data.method.annotation.SchemaMapping;
import org.springframework.stereotype.Controller;
@Controller
@SchemaMapping(typeName = "MusicQueries") (1)
public class MusicController {
@QueryMapping (2)
public MusicQueries music() {
return new MusicQueries();
}
(3)
public record MusicQueries() {
}
@SchemaMapping (4)
public Album album(@Argument String id) {
return new Album(id, "Spring GraphQL");
}
@SchemaMapping
public List<Artist> searchForArtist(@Argument String name) {
return List.of(new Artist("100", "the Spring team"));
}
}
| 1 | 使用 @SchemaMapping 和 typeName 属性注释控制器,以避免在方法上重复 |
| 2 | 为“music”命名空间定义 @QueryMapping |
| 3 | “music”查询返回一个“空”记录,但也可能返回一个空映射 |
| 4 | 查询现在被声明为“MusicQueries”类型下的字段 |
您可以在运行时通过 GraphQlSourceBuilderCustomizer 在 Spring Boot 中配置包装类型(“MusicQueries”、“UserQueries”),而无需在控制器中显式声明它们
import java.util.Collections;
import java.util.List;
import org.springframework.boot.graphql.autoconfigure.GraphQlSourceBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class NamespaceConfiguration {
@Bean
public GraphQlSourceBuilderCustomizer customizer() {
List<String> queryWrappers = List.of("music", "users"); (1)
return (sourceBuilder) -> sourceBuilder.configureRuntimeWiring((wiringBuilder) ->
queryWrappers.forEach((field) -> wiringBuilder.type("Query",
(builder) -> builder.dataFetcher(field, (env) -> Collections.emptyMap()))) (2)
);
}
}
| 1 | 列出“Query”类型的所有包装类型 |
| 2 | 手动为每个类型声明数据获取器,返回一个空映射 |