Web应用全栈之旅-Spring篇(四)集成GraphQL

1,329 阅读4分钟

banner窄.png

铿然架构  |  作者  /  铿然一叶 这是铿然架构的第 17 篇原创文章

相关阅读:

萌新快速成长之路
如何编写软件设计文档
JAVA编程思想(一)通过依赖注入增加扩展性
JAVA编程思想(二)如何面向接口编程
JAVA编程思想(三)去掉别扭的if,自注册策略模式优雅满足开闭原则
JAVA编程思想(四)Builder模式经典范式以及和工厂模式如何选?
Java编程思想(七)使用组合和继承的场景
JAVA基础(一)简单、透彻理解内部类和静态内部类
JAVA基础(二)内存优化-使用Java引用做缓存
JAVA基础(三)ClassLoader实现热加载
JAVA基础(四)枚举(enum)和常量定义,工厂类使用对比
JAVA基础(五)函数式接口-复用,解耦之利刃
Seata源码(一)初始化
Seata源码(二)事务基础对象
Seata源码(三)事务处理类结构和流程
Seata源码(四)全局锁GlobalLock
Seata源码(五)Seata数据库操作
Seata源码(六)Seata的undo日志操作
Seata源码(七)Seata事务故障处理
Seata源码(八)Seata事务生命周期hook
Seata源码(九)TCC核心类和处理逻辑
Seata源码(十)RM接收到请求后的调用过程
Seata源码(十一)TC接收到请求后的处理过程\


一、背景

当前端应用跨多端时,常碰到的问题有:

  1. 各端需要获取的信息量不同,例如手机端由于显示屏的大小需要获取的数据比PC端少,如果和PC端共用相同的接口,那么返回了很多冗余的数据,影响性能。
  2. 各端对数据的要求不同,有时需要聚合多个后端服务的数据,每一个聚合场景都新增一个服务会导致前端和后端耦合太紧;如果分多次调用又增加了前后端交互次数,从而影响性能。

为了解决这些问题GraphQL应运而生,其对应的能力为:

  1. 可以在发送查询请求时指定要返回哪些数据,未指定的不返回,满足了手机端只需要少量数据的要求。
  2. 可以在一次查询中同时请求多个后端服务,将多个服务的查询结果聚合后返回,而不需要新增聚合服务或者多次调用不同的后端服务后得到想要的数据。

有了以上能力,前端就可以根据已有的后端服务和实际业务要求灵活定制查询请求,不用再去麻烦后端哥哥开发新的服务了。

二、Spring集成GraphQL

  1. pom文件添加以下依赖:
        <!-- https://mvnrepository.com/artifact/com.graphql-java/graphql-spring-boot-starter -->
        <dependency>
            <groupId>com.graphql-java</groupId>
            <artifactId>graphql-spring-boot-starter</artifactId>
            <version>5.0.2</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.graphql-java/graphql-java-tools -->
        <dependency>
            <groupId>com.graphql-java</groupId>
            <artifactId>graphql-java-tools</artifactId>
            <version>5.2.4</version>
        </dependency>
		
		<!-- https://mvnrepository.com/artifact/com.graphql-java/graphiql-spring-boot-starter -->
		<dependency>
		    <groupId>com.graphql-java</groupId>
		    <artifactId>graphiql-spring-boot-starter</artifactId>
		    <version>5.0.2</version>
		</dependency>
  1. 查询服务
    需要支持GraphQL的查询服务要实现GraphQLQueryResolver接口,该接口仅仅是一个声明接口,没有任何方法。参考代码如下:
@Component
public class BookQueryResolver implements GraphQLQueryResolver {

	public List<Book> findBooks() {
		Author author = new Author(1, "Lee", 30);
		Book book = new Book(1, "Java 8实战", author, "电子工业出版社");
		List<Book> bookList = new ArrayList<Book>();
		bookList.add(book);
		return bookList;
	}

	public Book findBook(Integer id) {
		if (id == 1) {
		Author author = new Author(1, "Lee", 30);
		Book book = new Book(1, "Java 8实战", author, "电子工业出版社");
		return book;
		} else {
			return null;
		}
	}

	public Dog findDog(String name) {
		if (null != name && name.equals("xiaofei")) {
			return new Dog("xiaofei", 3, "male");
		} else {
			return null;
		}
	}
}

可以看到类上需要添加@Component注解。

  1. 配置GraphQL的schema
    schema配置文件可以放到resources目录下,为了支持查询有两类配置:
    1)数据对象配置,这些数据对象和查询服务返回的对象对应,字段可以比实际返回对象少,没有定义的则不返回。
type Author {
    id: Int
    name: String
    age: Int
}

type Book {
    id: Int
    name: String
    author: Author
    publisher: String
}

type Dog {
    name: String
    age: Int
    gender: String
}
  1. 查询方法配置,这些查询方法和实际查询方法对应,入参和出参保持一致。
type Query {
    findBooks: [Book]
    findBook(id:Int):Book
    findDog(name: String!):Dog
    findDogByName(name: String!):Dog
}

以上完成之后就可以使用查询服务了。

三、测试工具

GraphQL自带了一个测试工具,浏览器中输入: http://localhost:7000/graphiql 打开测试工具界面。(注:端口号为spring端口号,请自行修改)。


左边是查询请求,右边是返回结果,可以看到在查询请求中:

  1. 指定了查询参数
  2. 指定了要返回的字段
  3. 聚合了findBook和findDogByName两个后端服务

完美实现了之前我们提到的两点要求。

四、和Controler共存

在引入GraphQL之前,spring中前端和后端的接口通过Controller连接,如果Controller能被复用,不需要再重新写QueryResolver那不是完美了,实践证明,两者可以共存,只要Controller类实现GraphQLQueryResolver接口则可,这样既可以支持以前的Rest接口查询,又可以支持GraphQL查询。参考代码如下:

@RestController
public class QueryController implements GraphQLQueryResolver {
	@RequestMapping("/findDogByName")
	public Dog findDogByName(@RequestBody String name) throws Exception {
		if (null != name && name.equals("xiaofei")) {
			return new Dog("xiaofei", 3, "male");
		} else {
			return null;
		}
	}
}

end.

完整实例代码扫码加入微信公众号并回复:webfullstack,获取仓库地址。

<---左边点赞!