|
此版本仍在开发中,尚未被认为是稳定的。请使用最新稳定版本 Spring GraphQL 2.0.2! |
带注解的控制器
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定义标准的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
`0` 注解将处理器方法映射到GraphQL模式中的一个字段,并声明它是该字段的 `DataFetcher`。此注解可以指定父类型名称和字段名称:
@Controller
public class BookController {
@SchemaMapping(typeName="Book", field="author")
public Author getAuthor(Book book) {
// ...
}
}
`0` 注解也可以省略这些属性,在这种情况下,字段名称默认为方法名,而类型名称默认为注入到方法中的源/父对象的简单类名。例如,下面的内容默认类型为 "Book" 和字段为 "author":
@Controller
public class BookController {
@SchemaMapping
public Author author(Book book) {
// ...
}
}
`0` 注解可以在类级别声明,以指定该类中所有处理器方法的默认类型名称。
@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 处理方法具有灵活的签名,并可以从一系列方法参数和返回值中进行选择。
方法参数
Schema 映射处理方法可以有以下任意方法参数:
| 方法参数 | 描述 |
|---|---|
|
对绑定到更高层次、类型化对象的命名字段参数进行访问。 请参阅 |
|
对于原始参数值的访问。 请参阅 |
|
对于绑定到更高层次的类型化对象且带有指示输入参数是否省略(未设置)而非设置为 请参阅 |
|
对所有绑定到更高层级类型对象的字段参数的访问。 请参阅 |
|
对于参数的原始映射的访问权限。 |
|
通过项目接口访问字段参数。 |
"源" |
对于字段的源(即父/容器)实例的访问。 见 源码。 |
|
用于分页参数的访问。 |
|
对于排序详细信息的访问。 |
|
对于访问 请参阅 |
|
从主 |
|
从 |
|
从 |
|
从 Spring Security上下文获取(如果可用的话)。 |
|
从Spring Security上下文中访问 |
|
通过 |
|
从 |
|
直接访问底层的 |
返回值
Schema mapping handler方法可以返回:
| 返回值 | 描述 |
|---|---|
已解析的值 |
任何应用程序类型直接解析。 |
|
异步值(或多个值)。 支持控制器方法以及如 响应式 |
Kotlin |
它们会自动适应 |
|
异步生产值。
为了实现这一点, |
|
使用 这便是GraphQL Java的“完整”返回值,不仅包含“数据”。 可用于完成结果中的“扩展”或一个本地上下文。 |
在 Java 21+ 中,当配置了AnnotatedControllerConfigurer和Executor时,具有阻塞方法签名的控制器方法将异步调用。默认情况下,如果一个控制器方法不返回诸如Flux、Mono、CompletableFuture等异步类型,并且也不是 Kotlin 挂起函数,则该方法被视为阻塞方法。你可以通过在Predicate上配置AnnotatedControllerConfigurer来帮助确定哪些方法被认为是阻塞的。
The Spring Boot starter for Spring for GraphQL automatically configures
AnnotatedControllerConfigurer with an Executor for virtual threads when the property
spring.threads.virtual.enabled is set. |
接口架构映射
当控制器方法被映射到模式接口字段时,默认情况下该映射会被替换为多个映射,一个对应于实现该接口的每个模式对象类型。这使得可以使用一个控制器方法来处理所有子类型。
例如,给定:
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注解将参数绑定到目标对象并注入处理方法中。绑定过程是通过将参数值映射到预期的方法参数类型的主要数据构造函数来完成的,或者首先使用默认构造函数创建对象,然后将参数值映射到其属性。这个过程会递归地进行,利用所有嵌套的参数值和相应的创建嵌套的目标对象。例如:
@Controller
public class BookController {
@QueryMapping
public Book bookById(@Argument Long id) {
// ...
}
@MutationMapping
public Book addBook(@Argument BookInput bookInput) {
// ...
}
}
如果目标对象没有setter方法,且你无法修改这一点,你可以使用AnnotatedControllerConfigurer上的一个属性来允许通过直接字段访问进行绑定。 |
默认情况下,如果方法参数名称可用(需要使用 Java 8+ 的 -parameters 编译器标志或编译器提供的调试信息),则会使用该名称来查找参数。如需自定义名称,可以通过注解进行设置,例如 @Argument("bookInput")。
The @Argument annotation does not have a |
如果绑定失败,会抛出一个 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。 |
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 方法初始化,包括在顶级对象下方的任何层级嵌套的对象中作为字段。
@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();
}
源代码
在GraphQL Java中,`0`提供了对字段的源(即父级/容器)实例的访问。要访问这一点,请声明一个预期目标类型的方法参数。
@Controller
public class BookController {
@SchemaMapping
public Author author(Book book) {
// ...
}
}
源方法参数也有助于确定映射的类型名称。
如果 Java 类的简单名称与 GraphQL 类型匹配,则无需在 @Autowired 注解中显式指定类型名称。
|
一个 |
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;
}
然后可以使用@ModelAttribute注解控制器方法参数,在方法调用前对其进行验证:
@Controller
public class BookController {
@MutationMapping
public Book addBook(@Argument @Valid BookInput bookInput) {
// ...
}
}
如果在验证过程中发生错误,会抛出一个 ConstraintViolationException。
你可以通过使用 异常链(Exceptions) 来决定如何将该错误呈现给客户端,将其转换为GraphQL响应中包含的错误信息。
除了使用@Valid,你还可以使用Spring的@Validated来指定验证组。 |
Bean 验证适用于 @Argument、
@Arguments 和
@ProjectedPayload
方法参数,但也更广泛地适用于任何方法参数。
|
验证与Kotlin协程
Hibernate Validator 与 Kotlin 协程方法不兼容,并在反查其方法参数时失败。请参阅 spring-projects/spring-graphql#344 (评论) 以获取相关问题的链接和建议的工作around。 |
本地上下文
The main 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来延迟单个实体实例的加载,从而可以一起加载。例如:
@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());
}
}
对于上面展示的直接加载关联实体的情况,`0` 方法除了委托给 `1` 方法之外不做其他事情。这是一些可以使用 `@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为一个
|
返回值
批量映射方法可以返回:
| 返回类型 | 描述 |
|---|---|
|
以父对象作为键,批量加载的对象作为值的映射。 |
|
必须按与传递给方法的源/父对象相同顺序加载的一系列批处理对象。 |
|
不使用远程调用的命令式变体。 |
|
异步调用的命令式变体。为了使这一点生效,
|
Kotlin 协程与 |
已适应为 |
在 Java 21+ 中,当配置了AnnotatedControllerConfigurer和Executor时,具有阻塞方法签名的控制器方法将异步调用。默认情况下,如果一个控制器方法不返回诸如Flux、Mono、CompletableFuture等异步类型,并且也不是 Kotlin 挂起函数,则该方法被视为阻塞方法。你可以通过在Predicate上配置AnnotatedControllerConfigurer来帮助确定哪些方法被认为是阻塞的。
The Spring Boot starter for Spring for GraphQL automatically configures
AnnotatedControllerConfigurer with an Executor for virtual threads when the property
spring.threads.virtual.enabled is set. |
接口批量映射
当批次映射方法被映射到模式接口字段时,就像在 接口Schema映射 中的情况一样, 这种映射会被替换为多个映射,每个模式对象类型都实现该接口就对应一个映射。
这意味着,给定以下内容:
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实现的异常,而非基于控制器方法,则需要从DataFetcherExceptionResolver中获得一个AnnotatedControllerConfigurer对象,并将其注册在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 | 定义一个名为"音乐"的命名空间@QueryMapping |
| 3 | "music"查询返回一个"空"记录,但也可能返回一个空映射 |
| 4 | 查询现在作为字段声明在 "MusicQueries" 类型下 |
不再需要在控制器中显式声明包装类型("MusicQueries", "UserQueries"),
你可以选择使用一个GraphQlSourceBuilderCustomizer通过运行时配置来对其进行配置,这可以通过 Spring Boot 来实现:
import java.util.Collections;
import java.util.List;
import org.springframework.boot.autoconfigure.graphql.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 | 手动为每个它们声明数据获取器,并返回一个空的 Map |