Java 后端的未来? GraphQL?

共 7730字,需浏览 16分钟

 ·

2021-11-03 17:05

 GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。


003e2bcb221eb7a276adaca61af28e20.webp

GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。

GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。GraphQL 并没有和任何特定数据库或者存储引擎绑定,而是依靠你现有的代码和数据支撑.

以上是 GraphQL 的一些介绍和描述. 看了你可能会一脸懵逼, 没事, 我也是(哈哈)....... 因此, 我们先看使用后理解, 开始我们永久不衰的 Hello World.

1. 项目构建

1.1 创建项目

这里以** SpringBoot 2.1.2.RELEASE** 为例, 构建工具选用 Gradle, 快速创建一个 Web 项目. 需要的依赖有

dependencies {
implementation 'com.graphql-java:graphql-java:11.0'
implementation 'com.graphql-java:graphql-java-spring-boot-starter-webmvc:1.0'
implementation 'com.google.guava:guava:26.0-jre'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

功能实现一个书本及作者信息查询. 所有数据用集合模拟如下:

    private static List> 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> 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")
);

1.2 创建 Schema

resources 下新建 schema.graphqls 文件用于描述需要的 API.

type Query {
bookById(id: ID): Book
}

type Book {
id: ID
name: String
pageCount: Int
author: Author
}

type Author {
id: ID
firstName: String
lastName: String
}

该语句定义了两个实体: Book 和 Author, 并且定义了一个查询: bookById, 通过书本 id 查询 Book 信息, Book 中又包含 Author 信息, 因此需要级联查询 Author.

1.3 创建 GraphQL 实例

创建一个 GraphQLProvider.java 用于初始化和暴露 GraphQL 实例.

@Component
public class GraphQLProvider {

private GraphQL graphQL;

@Bean
public GraphQL graphQL() {
return graphQL;
}

@PostConstruct
public void init() throws IOException {
URL url = Resources.getResource("schema.graphqls");
String sdl = Resources.toString(url, Charsets.UTF_8);
GraphQLSchema graphQLSchema = buildSchema(sdl);
this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();
}

private GraphQLSchema buildSchema(String sdl) {
// TODO: 在这里构造 Schema
}
}

基于 schema.graphqls 构造 GraphQL 对象, 并暴露在 IOC 容器, GraphQL 适配器将会在此 GraphQL 对象之上构建 Schema 中的 API, 且默认的请求路径为 /graphql.

1.4 构建 GraphQLSchema

上面我们构建 GraphQL 实例还需要实现 buildSchema 方法, 此方法将会创建 GraphQLSchema 对象并获取数据.

    @Autowired
GraphQLDataFetchers graphQLDataFetchers;

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();
}

TypeDefinitionRegistry 是 Schema 文件的解析器。SchemaGenerator 结合 TypeDefinitionRegistryRuntimeWiring 来实际构造 GraphQLSchema. buildWiring 使用 graphQLDataFetchers 这个 Bean 来实际注册了两个 DataFetcher.

  • 一个通过书本 ID 查询书本的 DataFetcher

  • 一个获取书本中作者信息的 DataFetcher

DataFetcher 和如何创建 graphQLDataFetchers 稍后说明. 总的来说, 创建 GraphQLGraphQLSchema 的流程如下:

2a46760c66b7bf75d738b113d9572047.webp

1.5 DataFetcher

DataFetcher 是 GraphQL 最重要的概念之一, 用于查询时获取一个字段的数据. 当 GraphQL 执行查询时, 他会为每一个字段执行合适的 DataFetcher. DataFetcher 是一个只有一个方法的接口:

public interface DataFetcher {
T get(DataFetchingEnvironment dataFetchingEnvironment) throws Exception;
}

Schema 中的每个字段都需要一个 DataFetcher, 如果字段没有指定 DataFetcher, 那么就会使用的 PropertyDataFetcher 作为默认的 DataFetcher.

创建 1.4 节中的 graphQLDataFetchers 实现 GraphQLDataFetchers.java:

@Component
public class GraphQLDataFetchers {

private static List> 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> 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 book = dataFetchingEnvironment.getSource();
String authorId = book.get("authorId");
return authors
.stream()
.filter(author -> author.get("id").equals(authorId))
.findFirst()
.orElse(null);
};
}
}

这里我们创建了两个 DataFetcher: 通过 ID 获取书本信息的 getBookByIdDataFetcher 以及获取书本中作者信息的 getAuthorDataFetcher.

至此 GraphQL 工程就搭建起来了, 基本结构如下:

0e25adb06f7e3268791f9405d95e746a.webp

2. API 查询

启动 SpringBoot 应用 BookDetailsApplication 后, 我们就可以通过 http://localhost:8080/graphql 进行 API 访问. 你可以下载 GraphQL 查询工具查询(工具下载: https://github.com/prisma/graphql-playground), 如下:

7f95a54b75b7ace5b15baee342357486.webp

也可以通过 PostMan 查询, GraphQL 可以支持 GET 和 POST 两种请求查询.

2.1 Postman 通过 POST GraphQL 请求体查询

Postman 中请求体已经支持了 GraphQL, 查询语句如下:

{
bookById(id: "book-1"){
id
name
pageCount
author {
firstName
lastName
}
}
}

a411eb35ef792cbfb0af3e85cdc36af4.webp

2.2 Postman 通过普通 POST 请求查询

正常的 POST 请求也可以查询 GraphQL, 需要请求体携带一个 query 参数, 该参数即是上面的查询语句, 但是由于请求必须是 application/json, 而 JSON 对多行字符串不支持, 因此需要做好 JSON 处理

76139ecface9f531967b9fb0ac1c25ad.webp

由此也可以看出, GraphQL 请求体实际为我们做的就是请求参数的格式化.

2.3 Postman 通过 GET 请求查询

GraphQL 也支持 GET 方式查询, 主要是因为 GraphQLServlet 实现了 doGet 和 doPost 两个方法. Get 请求查询时需要一个 query 请求参数, 且参数值需要 encode.

a3017c4369b5a2d82512b46f981ecc43.webp

3. 应用场景

至此, 我们应该对 GraphQL 有一个简单的认识了, 那此时就有人会问了: 这玩意到底有啥用?

  • 静态变动态, 前端可以自己从 API 获取想要的数据,不必依赖 REST 端返回的固定数据结构

  • 请求次数及交互数据量的减少

  • API 网关层面

3b2c9d88a70b5bb5932e139e19fbbf20.webp

       

        如果有任何相关的问题都可以加入 QQ/微信群一起讨论, 学习, 进步. 此外如果有任何对于本公众号的意见和建议也欢迎大家留言积极批评指正, 最后, 愿你我都能成为更好的自己. 


        我是帅帅, 一个集帅气, 幽默与内涵, 并且热爱编程, 拥抱开源, 喜欢烹饪与旅游的暖男, 我们下期再见. 拜了个拜!

        老规矩别忘了哦, 点击原文链接跳转到我们官方的博客平台哦.



悄悄话




每文一句



Before you talk, LISTEN

Before you react, WAIT

Before you quit, TRY 


日常求赞

      你们白漂的力量就是我拖更的史诗级动力, 点赞, 评论, 再看, 赞赏, 看都看到这了, 随便点一个咯.



关注加好友


拉你进大佬交流群





浏览 56
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报