Skip to content

Commit acf1f30

Browse files
committed
Document GraalVM Native support
This commit documents the GraalVM Native image support in Spring for GraphQL and lists relevant pointers to the Spring Framework and Spring Boot documentations. This also explains how developers are expected to provide reachability hints for the cases where the AOT processing is not able to discover the relevant types. Closes gh-581
1 parent decbc40 commit acf1f30

File tree

11 files changed

+273
-5
lines changed

11 files changed

+273
-5
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ ext {
66
moduleProjects = [project(":spring-graphql"), project(":spring-graphql-test")]
77
springFrameworkVersion = "6.0.3"
88
graphQlJavaVersion = "19.2"
9-
bootVersion = "3.0.0"
9+
springBootVersion = "3.0.0"
1010
}
1111

1212
description = "Spring for GraphQL"

spring-graphql-docs/build.gradle

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ dependencies {
2323
api 'org.springframework:spring-webmvc'
2424
api 'org.springframework:spring-websocket'
2525
api 'org.springframework:spring-messaging'
26+
api 'org.springframework.data:spring-data-commons'
2627
asciidoctorExtensions 'io.spring.asciidoctor.backends:spring-asciidoctor-backends:0.0.3'
2728
}
2829

@@ -47,7 +48,7 @@ repositories {
4748
ext.javadocLinks = [
4849
"https://docs.oracle.com/javase/8/docs/api/",
4950
"https://javadoc.io/doc/com.graphql-java/graphql-java/${graphQlJavaVersion}/",
50-
"https://docs.spring.io/spring-boot/docs/${bootVersion}/api/",
51+
"https://docs.spring.io/spring-boot/docs/${springBootVersion}/api/",
5152
"https://docs.spring.io/spring-framework/docs/5.3.x/javadoc-api/"
5253
] as String[]
5354

@@ -102,7 +103,8 @@ asciidoctor {
102103
}
103104
outputDir "$buildDir/docs/reference/html"
104105
attributes 'spring-graphql-version': project.version,
105-
'spring-boot-version': bootVersion
106+
'spring-boot-version': springBootVersion,
107+
'spring-framework-version': springFrameworkVersion
106108
}
107109

108110
asciidoctor.mustRunAfter "check"

spring-graphql-docs/src/docs/asciidoc/attributes.adoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@
2121
:github-wiki: https://github.com/{github-repo}/wiki
2222
:graphql-java-docs: https://www.graphql-java.com/documentation
2323
:javadoc: https://docs.spring.io/spring-graphql/docs/{spring-graphql-version}/api
24-
:spring-framework-ref-docs: https://docs.spring.io/spring-framework/docs/current/reference/html
25-
// {spring-boot-version} attribute from build.gradle
24+
// version attributes from main build.gradle
25+
:spring-framework-ref-docs: https://docs.spring.io/spring-framework/docs/{spring-framework-version}/reference/html
2626
:spring-boot-ref-docs: https://docs.spring.io/spring-boot/docs/{spring-boot-version}/reference/html
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
[[graalvm]]
2+
= GraalVM Native support
3+
4+
Spring Framework 6.0 introduced the support infrastructure for compiling Spring applications to https://www.graalvm.org/22.3/reference-manual/native-image/[GraalVM Native images].
5+
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,
6+
please refer to the dedicated {spring-boot-ref-docs}/native-image.html#native-image[Spring Boot 3.0 GraalVM Native Image support documentation].
7+
Spring Boot also documents the https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-with-GraalVM[know limitations with the GraalVM support in Spring].
8+
9+
10+
[[graalvm.graphql-java]]
11+
== GraphQL Java metadata
12+
13+
Since the {spring-boot-ref-docs}/native-image.html#native-image.introducing-graalvm-native-images.key-differences-with-jvm-deployments[static analysis of your application is done at build time],
14+
GraalVM might need extra hints if your application is looking up static resources, performing reflection or creating JDK proxies at runtime.
15+
16+
GraphQL Java is performing three tasks at runtime that Native Images are sensible to:
17+
18+
1. Loading resource bundles for message internationalization
19+
2. Some reflection on internal types for schema inspection
20+
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
21+
22+
The first two items are handled via reachability metadata that has been contributed by the Spring team to
23+
https://github.com/oracle/graalvm-reachability-metadata/tree/master/metadata/com.graphql-java/graphql-java[the GraalVM reachability metadata repository].
24+
This metadata is automatically fetched by the native compilation tool when building an application that depends on GraphQL Java.
25+
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.
26+
27+
28+
[[graalvm.server]]
29+
== Native Server applications support
30+
31+
In typical Spring for GraphQL applications, Java types tied to the GraphQL schema are exposed in `@Controller` method signatures
32+
as parameters or return types. During the {spring-framework-ref-docs}/core.html#core.aot[Ahead Of Time processing phase] of the build,
33+
Spring or GraphQL will use its `o.s.g.data.method.annotation.support.SchemaMappingBeanFactoryInitializationAotProcessor` to discover
34+
the relevant types and register reachability metadata accordingly.
35+
This is all done automatically for you if you are building a Spring Boot application with GraalVM support.
36+
37+
If your application is "manually" registering data fetchers, some types are not discoverable as a result.
38+
You should then register them with Spring Framework's `@RegisterReflectionForBinding`:
39+
40+
include::code:GraphQlConfiguration[]
41+
<1> This application declares a `RuntimeWiringConfigurer` that "manually" adds a `DataFetcher`
42+
<2> Through this `DataFetcher`, the `BookRepository` will expose a `Book` type
43+
<3> `@RegisterReflectionForBinding` will register the relevant hints for the `Book` type and all types exposed as fields
44+
45+
[[graalvm.client]]
46+
== Client support
47+
48+
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.
49+
The `AotProcessor` strategy described in the section above cannot be used as a result.
50+
For client support, Spring for GraphQL embeds the {github-main-branch}/spring-graphql/src/main/resources/META-INF/native-image/org.springframework.graphql/spring-graphql[relevant reachability metadata for the client infrastructure].
51+
When it comes to Java types used by the application, applications should use a similar strategy as "manual" data fetchers using `@RegisterReflectionForBinding`:
52+
53+
include::code:ProjectService[]
54+
<1> In a Native image, we need to ensure that reflection can be performed on `Project` at runtime
55+
<2> `@RegisterReflectionForBinding` will register the relevant hints for the `Project` type and all types exposed as fields
56+

spring-graphql-docs/src/docs/asciidoc/index.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1672,6 +1672,7 @@ include::includes/client.adoc[leveloffset=+1]
16721672

16731673
include::includes/testing.adoc[leveloffset=+1]
16741674

1675+
include::includes/graalvm-native.adoc[leveloffset=+1]
16751676

16761677

16771678

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright 2020-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.graphql.docs.graalvm.client;
18+
19+
import java.util.List;
20+
21+
public record Project(String slug, String name, List<Releases> releases) {
22+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2020-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.graphql.docs.graalvm.client;
18+
19+
import reactor.core.publisher.Mono;
20+
21+
import org.springframework.aot.hint.annotation.RegisterReflectionForBinding;
22+
import org.springframework.graphql.client.GraphQlClient;
23+
import org.springframework.stereotype.Component;
24+
25+
@Component
26+
@RegisterReflectionForBinding(Project.class) // <2>
27+
public class ProjectService {
28+
29+
private final GraphQlClient graphQlClient;
30+
31+
public ProjectService(GraphQlClient graphQlClient) {
32+
this.graphQlClient = graphQlClient;
33+
}
34+
35+
public Mono<Project> project(String projectSlug) {
36+
String document = """
37+
query projectWithReleases($projectSlug: ID!) {
38+
project(slug: $projectSlug) {
39+
name
40+
releases {
41+
version
42+
}
43+
}
44+
}
45+
""";
46+
47+
return this.graphQlClient.document(document)
48+
.variable("projectSlug", projectSlug)
49+
.retrieve("project")
50+
.toEntity(Project.class); // <1>
51+
}
52+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2020-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.graphql.docs.graalvm.client;
18+
19+
public record Releases(String version) {
20+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2020-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.graphql.docs.graalvm.server;
18+
19+
public class Book {
20+
21+
Long id;
22+
23+
String name;
24+
25+
Long authorId;
26+
27+
public Book() {
28+
}
29+
30+
public Book(Long id, String name, Long authorId) {
31+
this.id = id;
32+
this.name = name;
33+
this.authorId = authorId;
34+
}
35+
36+
public Long getId() {
37+
return this.id;
38+
}
39+
40+
public void setId(Long id) {
41+
this.id = id;
42+
}
43+
44+
public String getName() {
45+
return this.name;
46+
}
47+
48+
public void setName(String name) {
49+
this.name = name;
50+
}
51+
52+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2020-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.graphql.docs.graalvm.server;
18+
19+
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
20+
import org.springframework.data.repository.CrudRepository;
21+
import org.springframework.graphql.data.GraphQlRepository;
22+
23+
@GraphQlRepository
24+
public interface BookRepository extends CrudRepository<Book, Long>, QuerydslPredicateExecutor<Book> {
25+
}

0 commit comments

Comments
 (0)