GraalVM ネイティブサポート

Spring Framework 6.0 は、Spring アプリケーションを GraalVM ネイティブイメージ (英語) にコンパイルするためのサポートインフラストラクチャを導入しました。一般的に GraalVM に精通していない場合、これが JVM にデプロイされたアプリケーションとどのように異なるか、および Spring アプリケーションでそれが何を意味するかについては、専用の Spring Boot 3.0 GraalVM ネイティブイメージのサポートドキュメントを参照してください。Spring Boot は、Spring での GraalVM サポートに関する既知の制限 [GitHub] (英語) もドキュメント化しています。

GraphQL Java メタデータ

アプリケーションの静的分析はビルド時に行われるため、アプリケーションが静的リソースを検索したり、リフレクションを実行したり、実行時に JDK プロキシを作成したりする場合、GraalVM は追加のヒントを必要とする場合があります。

GraphQL Java は、実行時にネイティブイメージが認識できる 3 つのタスクを実行します。

  1. メッセージの国際化のためのリソースバンドルの読み込み

  2. スキーマインスペクション の内部型に関する考察

  3. アプリケーションがスキーマに登録する Java 型の反映。これは、たとえば、GraphQL Java がアプリケーション型からプロパティを取得しているときに発生します。

最初の 2 つの項目は、Spring チームによって GraalVM 到達可能性メタデータリポジトリ [GitHub] (英語) に提供された到達可能性メタデータを介して処理されます。このメタデータは、GraphQL Java に依存するアプリケーションをビルドするときに、ネイティブコンパイルツールによって自動的に取得されます。リストの 3 番目の項目は対象外です。これらの型はアプリケーション自体によって提供され、別の方法で検出する必要があるためです。

ネイティブサーバーアプリケーションのサポート

典型的な Spring for GraphQL アプリケーションでは、GraphQL スキーマに結び付けられた Java 型は、パラメーターまたは戻り型として @Controller メソッドシグネチャーで公開されます。ビルドの Ahead Of Time 処理フェーズの間、Spring または GraphQL はその o.s.g.data.method.annotation.support.SchemaMappingBeanFactoryInitializationAotProcessor を使用して関連する型を検出し、それに応じて到達可能性メタデータを登録します。GraalVM をサポートする Spring Boot アプリケーションを構築している場合、これはすべて自動的に行われます。

アプリケーションがデータフェッチャーを「手動で」登録している場合、結果として一部の型が検出されません。次に、Spring Framework の @RegisterReflectionForBinding に登録する必要があります。

import graphql.schema.DataFetcher;

import org.springframework.aot.hint.annotation.RegisterReflectionForBinding;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.graphql.data.query.QuerydslDataFetcher;
import org.springframework.graphql.execution.RuntimeWiringConfigurer;

@Configuration
@RegisterReflectionForBinding(Book.class) (3)
public class GraphQlConfiguration {

	@Bean
	RuntimeWiringConfigurer customWiringConfigurer(BookRepository bookRepository) { (1)
		DataFetcher<Book> dataFetcher = QuerydslDataFetcher.builder(bookRepository).single();
		return (wiringBuilder) -> wiringBuilder
				.type("Query", (builder) -> builder.dataFetcher("book", dataFetcher)); (2)
	}

}
1 このアプリケーションは、DataFetcher を「手動で」追加する RuntimeWiringConfigurer を宣言します。
2 この DataFetcher を通じて、BookRepository は Book 型を公開します
3@RegisterReflectionForBinding は、Book 型およびフィールドとして公開されるすべての型に関連するヒントを登録します。

クライアントサポート

GraphQlClient は、必ずしもアプリケーションコンテキストで Bean として存在するわけではなく、メソッドシグネチャーのスキーマで使用される Java 型を公開しません。そのため、上記のセクションで説明した AotProcessor 戦略は使用できません。クライアントサポートのために、Spring for GraphQL はクライアントインフラストラクチャに関連する到達可能性メタデータ [GitHub] (英語) を埋め込みます。アプリケーションで使用される Java 型に関しては、アプリケーションは @RegisterReflectionForBinding を使用した「手動」データフェッチャーと同様の戦略を使用する必要があります。

import reactor.core.publisher.Mono;

import org.springframework.aot.hint.annotation.RegisterReflectionForBinding;
import org.springframework.graphql.client.GraphQlClient;
import org.springframework.stereotype.Component;

@Component
@RegisterReflectionForBinding(Project.class) (2)
public class ProjectService {

	private final GraphQlClient graphQlClient;

	public ProjectService(GraphQlClient graphQlClient) {
		this.graphQlClient = graphQlClient;
	}

	public Mono<Project> project(String projectSlug) {
		String document = """
				query projectWithReleases($projectSlug: ID!) {
					project(slug: $projectSlug) {
						name
						releases {
							version
						}
					}
				}
				""";

		return this.graphQlClient.document(document)
				.variable("projectSlug", projectSlug)
				.retrieve("project")
				.toEntity(Project.class); (1)
	}
}
1 ネイティブイメージでは、実行時に Project でリフレクションを実行できるようにする必要があります。
2@RegisterReflectionForBinding は、Project 型およびフィールドとして公開されるすべての型に関連するヒントを登録します。