註解控制器

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 預設為 QueryMutationSubscription。實際上,這些分別是 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 處理方法具有彈性的簽名,並且可以從一系列方法引數和傳回值中選擇。

方法引數

結構描述對應處理方法可以具有以下任何方法引數

方法引數 描述

@Argument

用於存取繫結到較高層級的類型化物件的具名字段引數。

請參閱 @Argument

@Argument Map<String, Object>

用於存取原始引數值。

請參閱 @Argument

ArgumentValue

用於存取繫結到較高層級的類型化物件的具名字段引數,以及指示輸入引數是被省略還是設定為 null 的旗標。

請參閱 ArgumentValue

@Arguments

用於存取繫結到較高層級的類型化物件的所有欄位引數。

請參閱 @Arguments

@Arguments Map<String, Object>

用於存取引數的原始 Map。

@ProjectedPayload 介面

用於透過投影介面存取欄位引數。

請參閱 @ProjectedPayload 介面

"Source"

用於存取欄位的來源(即父/容器)實例。

請參閱 Source

SubrangeScrollSubrange

用於存取分頁引數。

請參閱 分頁捲動Subrange

Sort

用於存取排序詳細資訊。

請參閱 分頁Sort

DataLoader

用於存取 DataLoaderRegistry 中的 DataLoader

請參閱 DataLoader

@ContextValue

用於存取 DataFetchingEnvironment 中主要 GraphQLContext 的屬性。

@LocalContextValue

用於存取 DataFetchingEnvironment 中本機 GraphQLContext 的屬性。

GraphQLContext

用於存取 DataFetchingEnvironment 中的內容。

java.security.Principal

從 Spring Security 內容取得(如果可用)。

@AuthenticationPrincipal

用於從 Spring Security 內容存取 Authentication#getPrincipal()

DataFetchingFieldSelectionSet

用於透過 DataFetchingEnvironment 存取查詢的選取集。

Locale, Optional<Locale>

用於存取 DataFetchingEnvironment 中的 Locale

DataFetchingEnvironment

用於直接存取底層的 DataFetchingEnvironment

傳回值

結構描述對應處理方法可以傳回

  • 任何類型的已解析值。

  • MonoFlux 用於非同步值。控制器方法和任何 DataFetcher 均支援,如 Reactive DataFetcher 中所述。

  • Kotlin 協程和 `Flow` 適應於 MonoFlux

  • java.util.concurrent.Callable 以非同步方式產生值。為了使其運作,AnnotatedControllerConfigurer 必須配置 Executor

在 Java 21+ 上,當 AnnotatedControllerConfigurer 配置了 Executor 時,具有封鎖方法簽名的控制器方法會以非同步方式調用。預設情況下,如果控制器方法未傳回非同步類型(例如 `Flux`、`Mono`、CompletableFuture),並且也不是 Kotlin 暫停函數,則該方法被視為封鎖方法。您可以在 AnnotatedControllerConfigurer 上配置封鎖控制器方法 `Predicate`,以協助判斷哪些方法被視為封鎖方法。

當設定屬性 spring.threads.virtual.enabled 時,Spring for GraphQL 的 Spring Boot starter 會自動使用虛擬執行緒的 `Executor` 配置 AnnotatedControllerConfigurer

介面結構描述對應

當控制器方法對應到結構描述介面欄位時,預設情況下,對應會替換為多個對應,每個實作介面的結構描述物件類型各一個。這允許針對所有子類型使用一個控制器方法。

例如,給定

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() {
		// ...
	}

	@SchemaMapping
	public User coordinator(Activity activity) {
		// Called for any Activity subtype
	}

}

如果需要,您可以接管個別子類型的對應

@Controller
public class BookController {

	@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 提供對欄位特定引數值 Map 的存取權。這些值可以是簡單的純量值(例如 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")

@Argument 註解沒有 "required" 旗標,也沒有指定預設值的選項。這兩者都可以在 GraphQL 結構描述層級指定,並由 GraphQL Java 強制執行。

如果繫結失敗,則會引發 BindException,且繫結問題累積為欄位錯誤,其中每個錯誤的 field 都是發生問題的引數路徑。

您可以將 @ArgumentMap<String, Object> 引數搭配使用,以取得引數的原始值。例如

@Controller
public class BookController {

	@MutationMapping
	public Book addBook(@Argument Map<String, Object> bookInput) {
		// ...
	}
}
在 1.2 之前,如果註解未指定名稱,則 @Argument Map<String, Object> 會傳回完整的引數 Map。在 1.2 之後,@ArgumentMap<String, Object> 始終傳回原始引數值,比對註解中指定的名稱或參數名稱。若要存取完整的引數 Map,請改用 @Arguments

ArgumentValue

預設情況下,GraphQL 中的輸入引數是可為 null 且可選的,這表示引數可以設定為 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

如果您想要將完整的引數 Map 繫結到單一目標物件,請使用 @Arguments 註解,這與繫結特定具名引數的 @Argument 相反。

例如,@Argument BookInput bookInput 使用引數 "bookInput" 的值來初始化 BookInput,而 @Arguments 使用完整的引數 Map,在這種情況下,最上層引數會繫結到 BookInput 屬性。

您可以將 @ArgumentsMap<String, Object> 引數搭配使用,以取得所有引數值的原始 Map。

@ProjectedPayload 介面

作為使用完整物件和 @Argument 的替代方案,您也可以使用投影介面透過明確定義的最小介面存取 GraphQL 請求引數。當 Spring Data 位於類路徑上時,Spring Data 的介面投影提供引數投影。

若要使用此功能,請建立使用 @ProjectedPayload 註解的介面,並將其宣告為控制器方法參數。如果參數使用 @Argument 註解,則它會套用至 `DataFetchingEnvironment.getArguments()` Map 中的個別引數。當在沒有 @Argument 的情況下宣告時,投影會作用於完整引數 Map 中的最上層引數。

例如

@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 註解中明確指定類型名稱。

給定來源/父書籍物件的清單,@BatchMapping 處理方法可以批次載入查詢的所有作者。

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),以取得相關問題的連結和建議的解決方法。

@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 也會透明地繫結到類型 Bookauthor 欄位,這只會針對作者委派給 DataLoader,並提供其來源/父 Book 實例。

若要用作唯一索引鍵,Book 必須實作 hashcodeequals

預設情況下,欄位名稱預設為方法名稱,而類型名稱預設為輸入 List 元素類型的簡單類別名稱。兩者都可以透過註解屬性自訂。類型名稱也可以從類別層級 @SchemaMapping 繼承。

方法引數

批次對應方法支援下列引數

方法引數 描述

List<K>

來源/父物件。

java.security.Principal

從 Spring Security 內容取得(如果可用)。

@ContextValue

用於存取 BatchLoaderEnvironmentGraphQLContext 中的值,這與來自 DataFetchingEnvironment 的內容相同。

GraphQLContext

用於存取 BatchLoaderEnvironment 中的內容,這與來自 DataFetchingEnvironment 的內容相同。

BatchLoaderEnvironment

在 GraphQL Java 中可用於 org.dataloader.BatchLoaderWithContext 的環境。

BatchLoaderEnvironmentcontext 屬性會傳回相同的 GraphQLContext 實例,該實例也可透過 DataFetchingEnvironment 用於 @SchemaMapping 方法。

當針對每個索引鍵呼叫 DataLoader 時,BatchLoaderEnvironmentkeyContexts 屬性會傳回從 DataFetchingEnvironment 取得的 localContext。

傳回值

批次對應方法可以傳回

傳回類型 描述

Mono<Map<K,V>>

以父物件作為索引鍵,批次載入物件作為值的 Map。

Flux<V>

批次載入物件的序列,必須與傳遞到方法中的來源/父物件順序相同。

Map<K,V>, Collection<V>

命令式變體,例如,無需進行遠端呼叫。

Callable<Map<K,V>>, Callable<Collection<V>>

要以非同步方式調用的命令式變體。為了使其運作,AnnotatedControllerConfigurer 必須配置 Executor

Kotlin 協程與 Map<K,V>,Kotlin Flow<K,V>

適應於 Mono<Map<K,V>Flux<V>

在 Java 21+ 上,當 AnnotatedControllerConfigurer 配置了 Executor 時,具有封鎖方法簽名的控制器方法會以非同步方式調用。預設情況下,如果控制器方法未傳回非同步類型(例如 `Flux`、`Mono`、CompletableFuture),並且也不是 Kotlin 暫停函數,則該方法被視為封鎖方法。您可以在 AnnotatedControllerConfigurer 上配置封鎖控制器方法 `Predicate`,以協助判斷哪些方法被視為封鎖方法。

當設定屬性 spring.threads.virtual.enabled 時,Spring for GraphQL 的 Spring Boot starter 會自動使用虛擬執行緒的 `Executor` 配置 AnnotatedControllerConfigurer

介面批次對應

介面結構描述對應 的情況一樣,當批次對應方法對應到結構描述介面欄位時,對應會替換為多個對應,每個實作介面的結構描述物件類型各一個。

這表示,給定以下

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 方法處理來自資料擷取的例外狀況,並具有彈性的 方法簽名。當在控制器中宣告時,例外狀況處理程式方法會套用至來自相同控制器的例外狀況

@Controller
public class BookController {

	@QueryMapping
	public Book bookById(@Argument Long id) {
		// ...
	}

	@GraphQlExceptionHandler
	public GraphQLError handle(BindException ex) {
		return GraphQLError.newError().errorType(ErrorType.BAD_REQUEST).message("...").build();
	}
}

當在 @ControllerAdvice 中宣告時,例外狀況處理程式方法會跨控制器套用

@ControllerAdvice
public class GlobalExceptionHandler {

	@GraphQlExceptionHandler
	public GraphQLError handle(BindException ex) {
		return GraphQLError.newError().errorType(ErrorType.BAD_REQUEST).message("...").build();
	}

}

透過 @GraphQlExceptionHandler 方法的例外狀況處理會自動套用至控制器調用。若要處理來自其他 graphql.schema.DataFetcher 實作的例外狀況(非基於控制器方法),請從 AnnotatedControllerConfigurer 取得 DataFetcherExceptionResolver,並將其在 GraphQlSource.Builder 中註冊為 DataFetcherExceptionResolver

方法簽名

例外狀況處理程式方法支援彈性的方法簽名,其方法引數是從 DataFetchingEnvironment 解析的,並且與 @SchemaMapping 方法 的引數相符。

支援的傳回類型如下所示

傳回類型 描述

graphql.GraphQLError

將例外狀況解析為單一欄位錯誤。

Collection<GraphQLError>

將例外狀況解析為多個欄位錯誤。

void

解析例外狀況,而不產生回應錯誤。

Object

將例外狀況解析為單一錯誤、多個錯誤或無錯誤。傳回值必須是 GraphQLErrorCollection<GraphQLError>null

Mono<T>

用於非同步解析,其中 <T> 是其中一種支援的同步傳回類型。

命名空間

在結構描述層級,查詢和變更操作直接在 QueryMutation 類型下定義。豐富的 GraphQL API 可以在這些類型下定義數十個操作,使其更難以探索 API 和區分關注點。您可以選擇 在 GraphQL 結構描述中定義命名空間。雖然此方法有一些注意事項,但您可以使用 Spring for GraphQL 註解控制器實作此模式。

透過命名空間,您的 GraphQL 結構描述可以(例如)將查詢操作巢狀在最上層類型下,而不是直接在 Query 下列出它們。在這裡,我們將定義 MusicQueriesUserQueries 類型,並使其在 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 使用 @SchemaMappingtypeName 屬性註解控制器,以避免在方法上重複它
2 為 "music" 命名空間定義 @QueryMapping
3 "music" 查詢傳回 "空的" 記錄,但也可能傳回空的 Map
4 查詢現在宣告為 "MusicQueries" 類型下的欄位

您可以選擇使用 Spring Boot 的 GraphQlSourceBuilderCustomizer,透過執行階段佈線配置包裝類型 ("MusicQueries"、"UserQueries"),而不是在控制器中明確宣告它們

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 手動宣告每個包裝器類型 的 data fetcher,傳回空的 Map