2. GraphQL 概述
GraphQL 是一种用于构建 Web 服务的新兴技术,作为 REST 的替代方案。最近涌现出多个 Java 库用于创建和调用 GraphQL 服务。
2.1. GraphQL 模式
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 的核心是请求对象上的特定字段。示例查询获取所有书籍标题:
{
"allBooks" {
"title"
}
}
服务器返回 JSON 格式响应:
{
"data": {
"allBooks": [
{
"title": "Title 1"
},
{
"title": "Title 2"
}
]
}
}
注意:变更(Mutation)用于修改操作,本文聚焦查询。
3. GraphQL 服务器实现
使用 GraphQL Java 库创建简单服务器:
3.1. 查询解析器实现
public class GraphQLQuery implements GraphQLQueryResolver {
private BookRepository repository;
public GraphQLQuery(BookRepository repository) {
this.repository = repository;
}
public List<Book> allBooks() {
return repository.getAllBooks();
}
}
3.2. 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();
}
}
3.3. 启动与测试
使用 Maven 插件运行:
mvn jetty:run
测试接口:
http://localhost:8080/graphql?query={allBooks{title}}
4. HTTP 客户端调用
GraphQL 服务通过 HTTP 暴露,可用任何 Java HTTP 客户端调用。
4.1. 发送请求
使用 Apache HttpClient 示例:
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);
}
替代方案:OkHttp、Java 11+ HttpClient 等均可使用
4.2. 解析响应
使用 Jackson 解析 JSON 响应:
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);
4.3. 模拟响应
使用 MockServer 模拟测试:
String requestQuery = "{allBooks{title}}";
String responseJson = "{\"data\":{\"allBooks\":[{\"title\":\"Title 1\"},{\"title\":\"Title 2\"}]}}";
new MockServerClient("127.0.0.1", 1080)
.when(
request()
.withPath("/graphql")
.withQueryStringParameter("query", requestQuery),
exactly(1)
)
.respond(
response()
.withStatusCode(HttpStatusCode.OK_200.code())
.withBody(responseJson)
);
5. 第三方库
5.1. American Express Nodes
基于模型定义构建查询的客户端:
Maven 依赖:
<dependency>
<groupId>com.github.americanexpress.nodes</groupId>
<artifactId>nodes</artifactId>
<version>0.5.0</version>
</dependency>
JitPack 仓库:
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
调用示例:
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);
}
5.2. GraphQL Java Generator
基于模式生成 Java 代码:
Maven 依赖:
<dependency>
<groupId>com.graphql-java-generator</groupId>
<artifactId>graphql-java-runtime</artifactId>
<version>1.18</version>
</dependency>
插件配置:
<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>com.baeldung.graphql.generated</packageName>
<copyRuntimeSources>false</copyRuntimeSources>
<generateDeprecatedRequestResponse>false</generateDeprecatedRequestResponse>
<separateUtilityClasses>true</separateUtilityClasses>
</configuration>
</plugin>
生成代码调用:
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 服务的完整流程:
核心能力:
- 创建基础 GraphQL 服务器
- 使用标准 HTTP 库发送请求
- JSON 响应解析为 Java 对象
- 服务模拟测试
第三方方案:
- Nodes:模型驱动的查询构建
- GraphQL Java Generator:模式代码生成
最佳实践:
- 优先使用类型安全的客户端库
- 复杂场景考虑代码生成方案
- 测试阶段善用 MockServer
踩坑提示:避免手动拼接查询字符串,推荐使用库提供的构建器防止注入风险
完整源码参考:GitHub 仓库