GraalVM Native support

在 Spring Framework 6.0 中引入了编译 Spring 应用程序为 GraalVM Native images 的支持基础设施。如果您不熟悉 GraalVM,它是如何与部署在 JVM 上的应用程序不同的以及这意味着什么对于 Spring 应用程序,请参考专门的 Spring Boot 3.0 GraalVM Native Image 支持文档。Spring Boot 还记录了 know limitations with the GraalVM support in Spring

Spring Framework 6.0 introduced the support infrastructure for compiling Spring applications to GraalVM Native images. If you are not familiar with GraalVM in general, how this differs from applications deployed on the JVM and what it means for Spring application, please refer to the dedicated Spring Boot 3.0 GraalVM Native Image support documentation. Spring Boot also documents the know limitations with the GraalVM support in Spring.

GraphQL Java metadata

由于 在构建时对您的应用程序进行了静态分析,如果您的应用程序在查找静态资源、执行反射或在运行时创建 JDK 代理,那么 GraalVM 可能需要额外的提示。

Since the static analysis of your application is done at build time, GraalVM might need extra hints if your application is looking up static resources, performing reflection or creating JDK proxies at runtime.

GraphQL Java 在运行时执行三项任务,而原生命像是对其敏感的:

GraphQL Java is performing three tasks at runtime that Native Images are sensible to:

  1. Loading resource bundles for message internationalization

  2. Some reflection on internal types for schema inspection

  3. Reflection on Java types that your application registers with the schema. This happens for example when GraphQL Java is fetching properties from application types

前两个项目是通过 Spring 团队贡献给 the GraalVM reachability metadata repository 的可访问性元数据处理的。当构建一个依赖于 GraphQL Java 的应用程序时,此元数据会被原生编译工具自动获取。这不能涵盖我们在列表中的第三个项目,因为这些类型是由应用程序本身提供的,并且必须通过另一种方式进行发现。

The first two items are handled via reachability metadata that has been contributed by the Spring team to the GraalVM reachability metadata repository. This metadata is automatically fetched by the native compilation tool when building an application that depends on GraphQL Java. This doesn’t cover our third item in the list, as those types are provided by the application itself and must be discovered by another mean.

Native Server applications support

在典型的适用于 GraphQL 的 Spring 应用程序中,与 GraphQL 架构绑定的 Java 类型在 @Controller 方法签名中公开为参数或返回类型。在构建的 提前处理阶段 期间,Spring 或 GraphQL 将使用其 o.s.g.data.method.annotation.support.SchemaMappingBeanFactoryInitializationAotProcessor 发现相关类型并相应地注册可访问性元数据。如果您使用 GraalVM 支持构建一个 Spring Boot 应用程序,则所有这些都将自动完成。

In typical Spring for GraphQL applications, Java types tied to the GraphQL schema are exposed in @Controller method signatures as parameters or return types. During the Ahead Of Time processing phase of the build, Spring or GraphQL will use its o.s.g.data.method.annotation.support.SchemaMappingBeanFactoryInitializationAotProcessor to discover the relevant types and register reachability metadata accordingly. This is all done automatically for you if you are building a Spring Boot application with GraalVM support.

如果你的应用程序“手动”注册数据提取器,那么某些类型不可发现。随后,你应使用 Spring Framework 的 @RegisterReflectionForBinding 注册它们:

If your application is "manually" registering data fetchers, some types are not discoverable as a result. You should then register them with Spring Framework’s @RegisterReflectionForBinding:

include-code::GraphQlConfiguration[]<1> 此应用程序声明了一个 RuntimeWiringConfigurer,它“手动”添加了一个 DataFetcher<2> 通过这个 DataFetcherBookRepository 将公开一个 Book 类型<3> @RegisterReflectionForBinding 将为 Book 类型和作为字段公开的所有类型注册相关提示

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 This application declares a RuntimeWiringConfigurer that "manually" adds a DataFetcher
2 Through this DataFetcher, the BookRepository will expose a Book type
3 @RegisterReflectionForBinding will register the relevant hints for the Book type and all types exposed as fields

Client support

GraphQlClient 不一定在应用程序上下文中作为 bean 存在,并且在方法签名中不会公开架构中使用的 Java 类型。因此,无法使用上面部分中描述的 AotProcessor 策略。对于客户端支持,适用于 GraphQL 的 Spring 嵌入了 客户端基础设施的相关可访问性元数据。当涉及到应用程序使用的 Java 类型时,应用程序应使用使用 @RegisterReflectionForBinding 的“手动”数据提取器:

The GraphQlClient is not necessarily present as a bean in the application context and it does not expose the Java types used in the schema in method signatures. The AotProcessor strategy described in the section above cannot be used as a result. For client support, Spring for GraphQL embeds the relevant reachability metadata for the client infrastructure. When it comes to Java types used by the application, applications should use a similar strategy as "manual" data fetchers using @RegisterReflectionForBinding:

include-code::ProjectService[]<1> 本机映像中,我们需要确保可以在运行时对 Project 执行反射<2> @RegisterReflectionForBinding 会注册与 Project 类型和所有公开为字段的类型相关的提示

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 In a Native image, we need to ensure that reflection can be performed on Project at runtime
2 @RegisterReflectionForBinding will register the relevant hints for the Project type and all types exposed as fields