从Java应用程序调用GraphQL服务

2025/04/21

1. 概述

GraphQL是一个相对较新的概念,用于构建Web服务作为REST的替代方案,最近出现了一些用于创建和调用GraphQL服务的Java库。

在本教程中,我们将了解GraphQL模式、查询和突变,我们介绍如何使用纯Java创建和Mock一个简单的GraphQL服务器。然后,我们将探讨如何使用众所周知的HTTP库调用GraphQL服务。

最后,我们还将探索用于调用GraphQL服务的可用第三方库。

2. GraphQL

GraphQL是一种用于Web服务的查询语言,也是一种使用类型系统执行查询的服务器端运行时

GraphQL服务器使用GraphQL模式指定API功能,这允许GraphQL客户端准确指定要从API检索的数据,这可能包括单个请求中的子资源和多个查询。

2.1 GraphQL模式

GraphQL服务器使用一组类型定义服务,这些类型描述了可以使用该服务查询的一组可能的数据。

GraphQL服务可以用任何语言编写,但是,需要使用称为GraphQL模式语言的DSL来定义GraphQL模式。

在我们的示例GraphQL模式中,我们将定义两种类型(Book和Author)和一个查询操作来获取所有书籍(allBooks):

type Book {
    title: String!
    author: Author
}

type Author {
    name: String!
    surname: String!
}

type Query {
    allBooks: [Book]
}

schema {
    query: Query
}

Query类型很特殊,因为它定义了GraphQL查询的入口点。

2.2 查询和突变

GraphQL服务是通过定义类型和字段,以及为不同字段提供函数来创建的

在最简单的形式中,GraphQL是关于请求对象上的特定字段。例如,我们可能会查询以获取所有书名:

{
    "allBooks" {
        "title"
    }
}

尽管看起来很相似,但这不是JSON。它是一种特殊的GraphQL查询格式,支持参数、别名、变量等。

GraphQL服务将使用JSON格式的响应来响应上述查询,如下所示:

{
    "data": {
        "allBooks": [
            {
                "title": "Title 1"
            },
            {
                "title": "Title 2"
            }
        ]
    }
}

在本教程中,我们将重点介绍如何使用查询获取数据。然而,重要的是要提到GraphQL中的另一个特殊概念-突变。

任何可能导致修改的操作都使用突变类型发送。

3. GraphQL服务器

让我们使用上面定义的模式在Java中创建一个简单的GraphQL服务器,我们将使用GraphQL Java库来实现我们的GraphQL服务器

我们从定义GraphQL查询开始,并实现示例GraphQL模式中指定的allBooks方法:

public class GraphQLQuery implements GraphQLQueryResolver {

    private BookRepository repository;

    public GraphQLQuery(BookRepository repository) {
        this.repository = repository;
    }

    public List<Book> allBooks() {
        return repository.getAllBooks();
    }
}

接下来,为了公开我们的GraphQL端点,我们创建一个Web Servlet:

@WebServlet(urlPatterns = "/graphql")
public class GraphQLEndpoint extends HttpServlet {

    private SimpleGraphQLHttpServlet graphQLServlet;

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        graphQLServlet.service(req, resp);
    }

    @Override
    public void init() {
        GraphQLSchema schema = SchemaParser.newParser()
              .resolvers(new GraphQLQuery(new BookRepository()))
              .file("schema.graphqls")
              .build()
              .makeExecutableSchema();
        graphQLServlet = SimpleGraphQLHttpServlet
              .newBuilder(schema)
              .build();
    }
}

在Servlet的init方法中,我们将解析位于resources文件夹中的GraphQL模式。最后,使用解析后的模式,我们可以创建SimpleGraphQLHttpServlet的实例。

我们将使用maven-war-plugin来打包我们的应用程序,并使用jetty-maven-plugin来运行它:

mvn jetty:run

现在我们准备好通过发送请求来运行和测试我们的GraphQL服务:

http://localhost:8080/graphql?query={allBooks{title}}

4. HTTP客户端

与REST服务一样,GraphQL服务通过HTTP协议公开。因此,我们可以使用任何Java HTTP客户端来调用GraphQL服务

4.1 发送请求

让我们尝试向我们在上一节中创建的GraphQL服务发送请求:

public static HttpResponse callGraphQLService(String url, String query) throws URISyntaxException, IOException {
    HttpClient client = HttpClientBuilder.create().build();
    HttpGet request = new HttpGet(url);
    URI uri = new URIBuilder(request.getURI())
        .addParameter("query", query)
        .build();
    request.setURI(uri);
    return client.execute(request);
}

在我们的示例中,我们使用了Apache HttpClient,但是可以使用任何Java HTTP客户端。

4.2 解析响应

接下来,让我们解析来自GraphQL服务的响应。GraphQL服务发送JSON格式的响应,与REST服务相同

HttpResponse httpResponse = callGraphQLService(serviceUrl, "{allBooks{title}}");
String actualResponse = IOUtils.toString(httpResponse.getEntity().getContent(), StandardCharsets.UTF_8.name());
Response parsedResponse = objectMapper.readValue(actualResponse, Response.class);
assertThat(parsedResponse.getData().getAllBooks()).hasSize(2);

在我们的示例中,我们使用了常用的Jackson库中的ObjectMapper。但是,我们也可以使用任何Java库进行JSON序列化/反序列化。

4.3 Mock响应

与通过HTTP公开的任何其他服务一样,我们可以Mock GraphQL服务器响应以进行测试

我们可以使用MockServer库来stubbing外部GraphQL HTTP服务:

String requestQuery = "{allBooks{title}}";
String responseJson = "{"data":{"allBooks":[{"title":"Title 1"},{"title":"Title 2"}]}}";

new MockServerClient(SERVER_ADDRESS, serverPort)
      .when(
            request()
                .withPath(PATH)
            .withQueryStringParameter("query", requestQuery),
      exactly(1)
    )
    .respond(
        response()
            .withStatusCode(HttpStatusCode.OK_200.code())
            .withBody(responseJson)
    );

我们的示例Mock服务器将接受GraphQL查询作为参数,并在正文中使用JSON响应进行响应。

5. 外部库

最近出现了一些允许更简单的GraphQL服务调用的Java GraphQL库。

5.1 American Express Nodes

Nodes是来自American Express的GraphQL客户端,设计用于从标准模型定义构建查询。要开始使用它,我们应该首先添加所需的依赖

<dependency>
    <groupId>com.github.americanexpress.nodes</groupId>
    <artifactId>nodes</artifactId>
    <version>0.5.0</version>>
</dependency>

该库目前托管在JitPack上,我们也应该将其添加到我们的Maven安装仓库中:

<repository>
    <id>jitpack.io</id>
    <url>https://jitpack.io</url>
</repository>

解决依赖关系后,我们就可以使用GraphQLTemplate构建查询并调用我们的GraphQL服务:

public static GraphQLResponseEntity<Data> callGraphQLService(String url, String query) throws IOException {
    GraphQLTemplate graphQLTemplate = new GraphQLTemplate();

    GraphQLRequestEntity requestEntity = GraphQLRequestEntity.Builder()
        .url(StringUtils.join(url, "?query=", query))
        .request(Data.class)
        .build();

    return graphQLTemplate.query(requestEntity, Data.class);
}

节点将使用我们指定的类解析来自GraphQL服务的响应:

GraphQLResponseEntity<Data> responseEntity = callGraphQLService(serviceUrl, "{allBooks{title}}");
assertThat(responseEntity.getResponse().getAllBooks()).hasSize(2);

我们应该注意到,Nodes仍然需要我们构建自己的DTO类来解析响应。

5.2 GraphQL Java Generator

GraphQL Java Generator库利用基于GraphQL模式生成Java代码的能力,这种方法类似于SOAP服务中使用的WSDL代码生成器

要开始使用它,我们应该首先添加所需的依赖

<dependency>
    <groupId>com.graphql-java-generator</groupId>
    <artifactId>graphql-java-runtime</artifactId>
    <version>1.18</version>
</dependency>

接下来,我们可以配置graphql-maven-plugin来执行generateClientCode目标:

<plugin>
    <groupId>com.graphql-java-generator</groupId>
    <artifactId>graphql-maven-plugin</artifactId>
    <version>1.18</version>
    <executions>
        <execution>
            <goals>
                <goal>generateClientCode</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <packageName>cn.tuyucheng.taketoday.graphql.generated</packageName>
        <copyRuntimeSources>false</copyRuntimeSources>
        <generateDeprecatedRequestResponse>false</generateDeprecatedRequestResponse>
        <separateUtilityClasses>true</separateUtilityClasses>
    </configuration>
</plugin>

一旦我们运行Maven构建命令,该插件将生成调用我们的GraphQL服务所需的DTO和实用程序类。

生成的QueryExecutor组件将包含调用我们的GraphQL服务和解析其响应的方法:

public List<Book> allBooks(String queryResponseDef, Object... paramsAndValues) throws GraphQLRequestExecutionException, GraphQLRequestPreparationException {
    logger.debug("Executing query 'allBooks': {} ", queryResponseDef);
    ObjectResponse objectResponse = getAllBooksResponseBuilder()
        .withQueryResponseDef(queryResponseDef).build();
    return allBooksWithBindValues(objectResponse, graphqlClientUtils.generatesBindVariableValuesMap(paramsAndValues));
}

但是,它是为与Spring框架一起使用而构建的。

6. 总结

在本文中,我们探讨了如何从Java应用程序调用GraphQL服务,我们学习了如何创建和Mock一个简单的GraphQL服务器。接下来,我们了解了如何使用标准HTTP库从GraphQL服务器发送请求和检索响应,并看到了如何将GraphQL服务响应从JSON解析为Java对象。

最后,我们介绍两个可用的第3方库来调用GraphQL服务,Nodes和GraphQLJavaGenerator。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章