GraphQL ist eine moderne Abfragesprache zum Abrufen von Daten von einem Server. Es gibt eine große Menge an Dokumentation zum Erstellen von APIs für eine ganze Reihe von Plattformen, aber leider enthält die offizielle Java-Dokumentation nur ein Beispiel, bei dem eine Anwendung basierend auf dem Spring Framework erstellt wird . Das Beispiel verbirgt einige Implementierungsdetails und zwingt den Benutzer, die Quelle zu lesen. In diesem Artikel werden wir dies beheben und ein Analogon zur Kombination von Google Guice und Spark erstellen . Bevor Sie fortfahren, empfehle ich Ihnen, sich mit dem Original- Tutorial vertraut zu machen , weil Ich werde nicht auf die Architektur der Bibliothek und die Beschreibung der Java GraphQL- Entitäten eingehen
1. Erstellen einer Guice-Anwendung
Gradle. , gradle init
:
Select type of project to generate: application
Select implementation language: Java
Select build script DSL: Kotlin
Select test framework: JUnit Jupiter
Project name: guice-spark-graphql
Source package: guice.spark.graphql
Java src/main/java/guice/spark/graphql/App.java
build.gradle.kts
dependencies {
// This dependency is used by the application.
implementation("com.google.guava:guava:29.0-jre")
//Guice
implementation("com.google.inject:guice:5.0.1") //NEW
//Spark
implementation("com.sparkjava:spark-core:2.9.3") //NEW
implementation("com.sparkjava:spark-template-velocity:2.7.1") ////NEW spark template engine
implementation("org.slf4j:slf4j-simple:1.7.21") //NEW fix Spark SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
implementation("com.fasterxml.jackson.core:jackson-databind:2.12.3") //NEW
//graphql
implementation("com.graphql-java:graphql-java:16.2") //NEW
// Use JUnit Jupiter API for testing.
testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.2")
// Use JUnit Jupiter Engine for testing.
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.6.2")
}
Guice, GraphQL Spark Slf4j, Jackson Velocity Template. Guava , -
2. GraphQL
GraphQL , API. schema.graphqls
src/main/resources
type Query { bookById(id: ID): Book } type Book { id: ID name: String pageCount: Int author: Author } type Author { id: ID firstName: String lastName: String }
3. Guice
Guice DI src/main/java/guice/spark/graphql/App.java
public class App {
@Inject
private GraphQLService service;
public static void main(String[] args) {
App app = new App();
Injector injector = Guice.createInjector(new GraphQLModule());
injector.injectMembers(app);
app.service.initialize();
}
}
Guice Injector, GraphQLModule . injector.injectMembers(app)
service GraphQLService
, ,
4. GraphQLModule
Guice . src/main/java/guice/spark/graphql/GraphQLModule.java
public class GraphQLModule extends AbstractModule {
protected void configure() {
bind(GraphQLService.class).asEagerSingleton();
bind(GraphQL.class).toProvider(GraphQlProvider.class).asEagerSingleton();
bind(ObjectMapper.class).asEagerSingleton();
}
}
configure
:
GraphQLService
Spark c Spark, GraphQL
GraphQL
ObjectMapper
JSON
5. GraphQlProvider
, GraphQL
, Provider
Guice, . GraphQlProvider
public class GraphQlProvider implements Provider<GraphQL> {
private GraphQLDataFetchers graphQLDataFetchers;
@Inject
public GraphQlProvider(GraphQLDataFetchers graphQLDataFetchers) {
this.graphQLDataFetchers = graphQLDataFetchers;
}
@Override
public GraphQL get() {
URL url = Resources.getResource("schema.graphqls");
String sdl = null;
try {
sdl = Resources.toString(url, Charsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
GraphQLSchema graphQLSchema = buildSchema(sdl);
return GraphQL.newGraphQL(graphQLSchema).build();
}
private GraphQLSchema buildSchema(String sdl) {
TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(sdl);
RuntimeWiring runtimeWiring = buildWiring();
SchemaGenerator schemaGenerator = new SchemaGenerator();
return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
}
private RuntimeWiring buildWiring() {
return RuntimeWiring.newRuntimeWiring()
.type(newTypeWiring("Query")
.dataFetcher("bookById", graphQLDataFetchers.getBookByIdDataFetcher()))
.type(newTypeWiring("Book")
.dataFetcher("author", graphQLDataFetchers.getAuthorDataFetcher()))
.build();
}
}
get() , GraphQL . ,
6. GraphQLDataFetchers
DataFetcher
GraphQL Java. DataFetcher - .
src/main/java/guice/spark/graphql/GraphQLDataFetchers.java
@Singleton
public class GraphQLDataFetchers {
private static List<Map<String, String>> books = Arrays.asList(
ImmutableMap.of("id", "book-1",
"name", "Harry Potter and the Philosopher's Stone",
"pageCount", "223",
"authorId", "author-1"),
ImmutableMap.of("id", "book-2",
"name", "Moby Dick",
"pageCount", "635",
"authorId", "author-2"),
ImmutableMap.of("id", "book-3",
"name", "Interview with the vampire",
"pageCount", "371",
"authorId", "author-3")
);
private static List<Map<String, String>> authors = Arrays.asList(
ImmutableMap.of("id", "author-1",
"firstName", "Joanne",
"lastName", "Rowling"),
ImmutableMap.of("id", "author-2",
"firstName", "Herman",
"lastName", "Melville"),
ImmutableMap.of("id", "author-3",
"firstName", "Anne",
"lastName", "Rice")
);
public DataFetcher getBookByIdDataFetcher() {
return dataFetchingEnvironment -> {
String bookId = dataFetchingEnvironment.getArgument("id");
return books
.stream()
.filter(book -> book.get("id").equals(bookId))
.findFirst()
.orElse(null);
};
}
public DataFetcher getAuthorDataFetcher() {
return dataFetchingEnvironment -> {
Map<String, String> book = dataFetchingEnvironment.getSource();
String authorId = book.get("authorId");
return authors
.stream()
.filter(author -> author.get("id").equals(authorId))
.findFirst()
.orElse(null);
};
}
}
@Singleton
Guice - . configure
GraphQLModule
, -
7. GraphQLService
API web . GraphQLService
src/main/java/guice/spark/graphql/GraphQLService.java
public class GraphQLService {
private final GraphQL graphQL;
private final ObjectMapper mapper;
@Inject
public GraphQLService(GraphQL graphQL, ObjectMapper mapper) {
this.graphQL = graphQL;
this.mapper = mapper;
}
public void initialize() {
post("/graphql", (request, response) -> {
GraphQLRequestBody body = mapper.readValue(request.body(), GraphQLRequestBody.class);
String query = body.getQuery();
if (query == null) {
query = "";
}
ExecutionResult executionResult = graphQL.execute(
ExecutionInput.newExecutionInput()
.query(query)
.operationName(body.getOperationName())
.variables(body.getVariables())
.build()
);
response.type("application/json");
return mapper.writeValueAsString(executionResult.toSpecification());
});
get("/playground", (req, res) -> new VelocityTemplateEngine().render(
new ModelAndView(Collections.emptyMap(), "playground.html"))
);
}
initialize
Jetty web server 4567. :
POST http://localhost:4567/graphql ObjectMapper
GraphQLRequestBody
GraphQL . JSON .
GraphQLRequestBody:
public class GraphQLRequestBody {
private String query;
private String operationName;
private Map<String, Object> variables;
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
public String getOperationName() {
return operationName;
}
public void setOperationName(String operationName) {
this.operationName = operationName;
}
public Map<String, Object> getVariables() {
return variables;
}
public void setVariables(Map<String, Object> variables) {
this.variables = variables;
}
}
GET http://localhost:4567/playground playground.html , src/main/resources
. API .
. http://localhost:4567/playground
API
. :
query {
bookById(id: "book-1") {
name,
author {
firstName
}
}
}
API:
{
"data": {
"bookById": {
"name": "Harry Potter and the Philosopher's Stone",
"author": {
"firstName": "Joanne"
}
}
}
}